diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index c061edd7a..696ee3277 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -8,6 +8,11 @@
=== Breaking changes
+- All services that were previously available in `org.eclipse.syson.sysml.helper` package of `syson-sysml-metamodel` module have been moved to `org.eclipse.syson.sysml.metamodel.helper` package of `syson-sysml-metamodel-services` module.
+- All services that were previously available in `org.eclipse.syson.sysml.textual` package of `syson-sysml-metamodel` module have been moved to `org.eclipse.syson.sysml.metamodel.services.textual` package of `syson-sysml-metamodel-services` module.
+- All services that were previously available in `org.eclipse.syson.sysml.textual.utils` package of `syson-sysml-metamodel` module have been moved to `org.eclipse.syson.sysml.metamodel.services.textual.utils` package of `syson-sysml-metamodel-services` module.
+- All services that were previously available in `org.eclipse.syson.sysml.util` package of `syson-sysml-metamodel` module have been moved to `org.eclipse.syson.sysml.metamodel.util` package of `syson-sysml-metamodel-services` module.
+
=== Dependency update
- [releng] Update to https://github.com/vitejs/vite/releases/tag/v8.0.13[Vite 8.0.13] and https://github.com/vitejs/vite-plugin-react/releases/tag/plugin-react%406.0.2[`@vitejs/plugin-react` 6.0.2].
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysMLv2RewriteProxiesResourcesFilter.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysMLv2RewriteProxiesResourcesFilter.java
index ce44cdcfa..cda329cb2 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysMLv2RewriteProxiesResourcesFilter.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysMLv2RewriteProxiesResourcesFilter.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -14,7 +14,7 @@
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.sirius.web.application.project.services.api.IRewriteProxiesResourceFilter;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.springframework.stereotype.Service;
/**
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysONDefaultLibrariesConfiguration.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysONDefaultLibrariesConfiguration.java
index 70cf6bb93..bd296f626 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysONDefaultLibrariesConfiguration.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysONDefaultLibrariesConfiguration.java
@@ -28,8 +28,8 @@
import org.eclipse.syson.application.libraries.SysONLibraryLoader;
import org.eclipse.syson.application.libraries.SysONLibraryLoadingDefinition;
import org.eclipse.syson.sysml.SysmlPackage;
-import org.eclipse.syson.sysml.helper.EMFUtils;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.helper.EMFUtils;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.eclipse.syson.sysml.util.LibraryNamespaceProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/index/SysONIndexUpdateService.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/index/SysONIndexUpdateService.java
index cc7e4072c..d48e518f3 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/index/SysONIndexUpdateService.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/index/SysONIndexUpdateService.java
@@ -30,7 +30,7 @@
import org.eclipse.sirius.web.infrastructure.elasticsearch.services.api.IIndexCreationService;
import org.eclipse.sirius.web.infrastructure.elasticsearch.services.api.IIndexDeletionService;
import org.eclipse.sirius.web.infrastructure.elasticsearch.services.api.IIndexUpdateServiceDelegate;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/DiagramOnViewUsageMigrationHook.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/DiagramOnViewUsageMigrationHook.java
index 39a6a8729..fe17981f9 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/DiagramOnViewUsageMigrationHook.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/DiagramOnViewUsageMigrationHook.java
@@ -37,7 +37,7 @@
import org.eclipse.syson.sysml.SysmlFactory;
import org.eclipse.syson.sysml.ViewDefinition;
import org.eclipse.syson.sysml.ViewUsage;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.eclipse.syson.util.GetIntermediateContainerCreationSwitch;
import org.eclipse.syson.util.StandardDiagramsConstants;
import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers;
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/OneDiagramDescriptionMigrationHook.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/OneDiagramDescriptionMigrationHook.java
index 93f2303d6..684d0f39b 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/OneDiagramDescriptionMigrationHook.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/OneDiagramDescriptionMigrationHook.java
@@ -30,7 +30,7 @@
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.services.api.IRepresentationMetadataUpdateService;
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData;
import org.eclipse.syson.sysml.Element;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.stereotype.Service;
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/StandardLibrariesElementsDiagramMigrationParticipant.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/StandardLibrariesElementsDiagramMigrationParticipant.java
index 335721665..0d8ed194c 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/StandardLibrariesElementsDiagramMigrationParticipant.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/StandardLibrariesElementsDiagramMigrationParticipant.java
@@ -18,7 +18,7 @@
import org.eclipse.sirius.components.core.api.IEditingContext;
import org.eclipse.sirius.components.core.api.IObjectSearchService;
import org.eclipse.syson.sysml.Element;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.springframework.stereotype.Service;
import tools.jackson.databind.JsonNode;
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/StandardLibrariesElementsReferencesMigrationParticipant.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/StandardLibrariesElementsReferencesMigrationParticipant.java
index ca12d5ce1..d657f06f9 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/StandardLibrariesElementsReferencesMigrationParticipant.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/migration/StandardLibrariesElementsReferencesMigrationParticipant.java
@@ -21,7 +21,7 @@
import org.eclipse.sirius.components.emf.migration.api.IMigrationParticipant;
import org.eclipse.sirius.emfjson.resource.JsonResource;
import org.eclipse.syson.sysml.Element;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.springframework.stereotype.Service;
/**
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/publication/SysONSysMLLibraryPublisher.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/publication/SysONSysMLLibraryPublisher.java
index 7631e1cfa..d55a72d2a 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/publication/SysONSysMLLibraryPublisher.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/publication/SysONSysMLLibraryPublisher.java
@@ -51,7 +51,7 @@
import org.eclipse.syson.application.publication.api.ISysONLibraryDependencyCollector;
import org.eclipse.syson.application.services.SysONResourceService;
import org.eclipse.syson.services.api.ISysONResourceService;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/search/SysONSearchService.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/search/SysONSearchService.java
index 9b084a55c..e81570f91 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/search/SysONSearchService.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/search/SysONSearchService.java
@@ -30,7 +30,7 @@
import org.eclipse.sirius.web.application.library.services.LibraryMetadataAdapter;
import org.eclipse.sirius.web.application.views.search.dto.SearchQuery;
import org.eclipse.sirius.web.application.views.search.services.api.ISearchService;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Primary;
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/DetailsViewService.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/DetailsViewService.java
index 3eb15621b..bd072b566 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/DetailsViewService.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/DetailsViewService.java
@@ -70,9 +70,9 @@
import org.eclipse.syson.sysml.Type;
import org.eclipse.syson.sysml.ViewUsage;
import org.eclipse.syson.sysml.metamodel.services.ElementInitializerSwitch;
-import org.eclipse.syson.sysml.textual.SysMLElementSerializer;
-import org.eclipse.syson.sysml.textual.SysMLSerializingOptions;
-import org.eclipse.syson.sysml.textual.utils.FileNameDeresolver;
+import org.eclipse.syson.sysml.metamodel.services.textual.SysMLElementSerializer;
+import org.eclipse.syson.sysml.metamodel.services.textual.SysMLSerializingOptions;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.FileNameDeresolver;
/**
* Java services needed to execute the AQL expressions used in the {@link SysMLv2PropertiesConfigurer}.
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONIdentityService.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONIdentityService.java
index 5b4143d93..9674f8562 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONIdentityService.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONIdentityService.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -19,7 +19,7 @@
import org.eclipse.sirius.components.emf.services.IDAdapter;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.Relationship;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.springframework.stereotype.Service;
/**
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegate.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegate.java
index e068c49dd..13e4766b8 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegate.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegate.java
@@ -19,7 +19,7 @@
import org.eclipse.sirius.components.core.api.IReadOnlyObjectPredicateDelegate;
import org.eclipse.sirius.components.emf.ResourceMetadataAdapter;
import org.eclipse.syson.sysml.Element;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.springframework.stereotype.Service;
/**
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONResourceService.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONResourceService.java
index eabbd7e1d..d839c3580 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONResourceService.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/SysONResourceService.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2025 Obeo.
+ * Copyright (c) 2025, 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
@@ -25,7 +25,7 @@
import org.eclipse.sirius.web.domain.boundedcontexts.library.services.api.ILibrarySearchService;
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData;
import org.eclipse.syson.services.api.ISysONResourceService;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.stereotype.Service;
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/sysmlv2/SysONDefaultResourceProvider.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/sysmlv2/SysONDefaultResourceProvider.java
index 1567876be..26d40878c 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/sysmlv2/SysONDefaultResourceProvider.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/sysmlv2/SysONDefaultResourceProvider.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -27,7 +27,7 @@
import org.eclipse.sirius.components.emf.services.JSONResourceFactory;
import org.eclipse.syson.application.sysmlv2.api.IDefaultSysMLv2ResourceProvider;
import org.eclipse.syson.sysml.SysmlFactory;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/update/SysONEditingContextSnapshotService.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/update/SysONEditingContextSnapshotService.java
index 72265c4eb..e39f324bb 100644
--- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/update/SysONEditingContextSnapshotService.java
+++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/update/SysONEditingContextSnapshotService.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2025 Obeo.
+ * Copyright (c) 2025, 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
@@ -29,7 +29,7 @@
import org.eclipse.sirius.web.application.editingcontext.services.api.IResourceToDocumentService;
import org.eclipse.sirius.web.application.library.services.LibraryMetadataAdapter;
import org.eclipse.syson.application.configuration.SysMLEditingContextProcessor;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
diff --git a/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/DetailsViewServiceTest.java b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/DetailsViewServiceTest.java
index 0f48b5c95..b533be80e 100644
--- a/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/DetailsViewServiceTest.java
+++ b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/DetailsViewServiceTest.java
@@ -30,7 +30,7 @@
import org.eclipse.syson.sysml.Package;
import org.eclipse.syson.sysml.SysmlFactory;
import org.eclipse.syson.sysml.SysmlPackage;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
diff --git a/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegateTests.java b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegateTests.java
index 7822b9758..b1cd500a9 100644
--- a/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegateTests.java
+++ b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/SysONReadOnlyObjectPredicateDelegateTests.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2025 Obeo.
+ * Copyright (c) 2025, 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
@@ -38,8 +38,8 @@
import org.eclipse.syson.sysml.OwningMembership;
import org.eclipse.syson.sysml.Package;
import org.eclipse.syson.sysml.SysmlFactory;
-import org.eclipse.syson.sysml.helper.EMFUtils;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.helper.EMFUtils;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
diff --git a/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/SysONResourceServiceTests.java b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/SysONResourceServiceTests.java
index 27ee3332a..7005ebe33 100644
--- a/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/SysONResourceServiceTests.java
+++ b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/SysONResourceServiceTests.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2025 Obeo.
+ * Copyright (c) 2025, 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
@@ -29,7 +29,7 @@
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData;
import org.eclipse.syson.services.api.ISysONResourceService;
import org.eclipse.syson.sysml.SysmlPackage;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.domain.Page;
diff --git a/backend/application/syson-application/pom.xml b/backend/application/syson-application/pom.xml
index aeb64a103..ceca248c4 100644
--- a/backend/application/syson-application/pom.xml
+++ b/backend/application/syson-application/pom.xml
@@ -1,6 +1,6 @@
org.springframework.boot
diff --git a/backend/application/syson-sysml-export/src/main/java/org/eclipse/syson/sysml/export/SysMLv2DocumentExporter.java b/backend/application/syson-sysml-export/src/main/java/org/eclipse/syson/sysml/export/SysMLv2DocumentExporter.java
index 425ee5698..1333302df 100644
--- a/backend/application/syson-sysml-export/src/main/java/org/eclipse/syson/sysml/export/SysMLv2DocumentExporter.java
+++ b/backend/application/syson-sysml-export/src/main/java/org/eclipse/syson/sysml/export/SysMLv2DocumentExporter.java
@@ -21,10 +21,10 @@
import org.eclipse.sirius.web.application.document.services.api.IDocumentExporter;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.impl.MembershipCacheAdapter;
-import org.eclipse.syson.sysml.textual.SysMLElementSerializer;
-import org.eclipse.syson.sysml.textual.SysMLSerializingOptions;
-import org.eclipse.syson.sysml.textual.utils.FileNameDeresolver;
-import org.eclipse.syson.sysml.textual.utils.Status;
+import org.eclipse.syson.sysml.metamodel.services.textual.SysMLElementSerializer;
+import org.eclipse.syson.sysml.metamodel.services.textual.SysMLSerializingOptions;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.FileNameDeresolver;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
diff --git a/backend/application/syson-sysml-export/src/main/java/org/eclipse/syson/sysml/export/SysMLv2EditingContextPersistenceFilter.java b/backend/application/syson-sysml-export/src/main/java/org/eclipse/syson/sysml/export/SysMLv2EditingContextPersistenceFilter.java
index 059e8a412..a1cb2dddc 100644
--- a/backend/application/syson-sysml-export/src/main/java/org/eclipse/syson/sysml/export/SysMLv2EditingContextPersistenceFilter.java
+++ b/backend/application/syson-sysml-export/src/main/java/org/eclipse/syson/sysml/export/SysMLv2EditingContextPersistenceFilter.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -14,7 +14,7 @@
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.sirius.web.application.editingcontext.services.api.IEditingContextPersistenceFilter;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.springframework.stereotype.Service;
/**
diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/ASTTransformer.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/ASTTransformer.java
index 38c897125..d8ca0d958 100644
--- a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/ASTTransformer.java
+++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/ASTTransformer.java
@@ -27,7 +27,7 @@
import org.eclipse.sirius.components.emf.services.JSONResourceFactory;
import org.eclipse.sirius.components.representations.Message;
import org.eclipse.syson.services.DeleteService;
-import org.eclipse.syson.sysml.helper.EMFUtils;
+import org.eclipse.syson.sysml.metamodel.helper.EMFUtils;
import org.eclipse.syson.sysml.parser.AstTreeParser;
import org.eclipse.syson.sysml.parser.ContainmentReferenceHandler;
import org.eclipse.syson.sysml.parser.EAttributeHandler;
diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/AstParsingResult.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/AstParsingResult.java
index 7a3c4fe3b..9412f1f4f 100644
--- a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/AstParsingResult.java
+++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/AstParsingResult.java
@@ -16,7 +16,7 @@
import java.util.List;
import java.util.Optional;
-import org.eclipse.syson.sysml.textual.utils.Status;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.Status;
/**
* Result of the parsing of the AST.
diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/SysmlToAst.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/SysmlToAst.java
index 8c20eabee..a5ed0a36f 100644
--- a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/SysmlToAst.java
+++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/SysmlToAst.java
@@ -29,8 +29,8 @@
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
-import org.eclipse.syson.sysml.textual.utils.Severity;
-import org.eclipse.syson.sysml.textual.utils.Status;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.Severity;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.Status;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/InsertTextualSysMLv2EventHandler.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/InsertTextualSysMLv2EventHandler.java
index 30b2ad919..2f923be72 100644
--- a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/InsertTextualSysMLv2EventHandler.java
+++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/InsertTextualSysMLv2EventHandler.java
@@ -34,7 +34,7 @@
import org.eclipse.syson.sysml.ASTTransformer;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.SysmlToAst;
-import org.eclipse.syson.sysml.textual.utils.Status;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.Status;
import org.springframework.stereotype.Service;
import io.micrometer.core.instrument.Counter;
diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/parser/AstTreeParser.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/parser/AstTreeParser.java
index c407ac8a2..7ba4e3ab2 100644
--- a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/parser/AstTreeParser.java
+++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/parser/AstTreeParser.java
@@ -28,7 +28,7 @@
import org.eclipse.syson.sysml.Redefinition;
import org.eclipse.syson.sysml.Specialization;
import org.eclipse.syson.sysml.SysmlPackage;
-import org.eclipse.syson.sysml.helper.EMFUtils;
+import org.eclipse.syson.sysml.metamodel.helper.EMFUtils;
import org.eclipse.syson.sysml.parser.translation.EClassifierTranslator;
import org.eclipse.syson.sysml.utils.LogNameProvider;
import org.eclipse.syson.sysml.utils.MessageReporter;
diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/parser/ProxyResolver.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/parser/ProxyResolver.java
index 300bbb85c..6a4083156 100644
--- a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/parser/ProxyResolver.java
+++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/parser/ProxyResolver.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -27,7 +27,7 @@
import org.eclipse.syson.sysml.Namespace;
import org.eclipse.syson.sysml.PortDefinition;
import org.eclipse.syson.sysml.SysmlPackage;
-import org.eclipse.syson.sysml.helper.DeresolvingNamespaceProvider;
+import org.eclipse.syson.sysml.metamodel.helper.DeresolvingNamespaceProvider;
import org.eclipse.syson.sysml.utils.LogNameProvider;
import org.eclipse.syson.sysml.utils.MessageReporter;
import org.slf4j.Logger;
diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/upload/SysMLExternalResourceLoaderService.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/upload/SysMLExternalResourceLoaderService.java
index 13437bedb..bd51f11a0 100644
--- a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/upload/SysMLExternalResourceLoaderService.java
+++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/upload/SysMLExternalResourceLoaderService.java
@@ -33,8 +33,8 @@
import org.eclipse.syson.sysml.ASTTransformer;
import org.eclipse.syson.sysml.AstParsingResult;
import org.eclipse.syson.sysml.SysmlToAst;
-import org.eclipse.syson.sysml.textual.utils.Status;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.Status;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
diff --git a/backend/application/syson-sysml-validation/src/main/java/org/eclipse/syson/sysml/validation/SysONSysMLValidationService.java b/backend/application/syson-sysml-validation/src/main/java/org/eclipse/syson/sysml/validation/SysONSysMLValidationService.java
index 76cca95b6..83c27220d 100644
--- a/backend/application/syson-sysml-validation/src/main/java/org/eclipse/syson/sysml/validation/SysONSysMLValidationService.java
+++ b/backend/application/syson-sysml-validation/src/main/java/org/eclipse/syson/sysml/validation/SysONSysMLValidationService.java
@@ -29,7 +29,7 @@
import org.eclipse.sirius.components.core.api.IValidationService;
import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext;
import org.eclipse.syson.services.api.ISysONResourceService;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
diff --git a/backend/metamodel/syson-sysml-metamodel-edit/pom.xml b/backend/metamodel/syson-sysml-metamodel-edit/pom.xml
index 4cfb40af0..e9996ff77 100644
--- a/backend/metamodel/syson-sysml-metamodel-edit/pom.xml
+++ b/backend/metamodel/syson-sysml-metamodel-edit/pom.xml
@@ -1,6 +1,6 @@
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/helper/DeresolvingNamespaceProvider.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/DeresolvingNamespaceProvider.java
similarity index 99%
rename from backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/helper/DeresolvingNamespaceProvider.java
rename to backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/DeresolvingNamespaceProvider.java
index 26168a523..2c6cf7b56 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/helper/DeresolvingNamespaceProvider.java
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/DeresolvingNamespaceProvider.java
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.helper;
+package org.eclipse.syson.sysml.metamodel.helper;
import static java.util.stream.Collectors.toList;
diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/EMFUtils.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/EMFUtils.java
new file mode 100644
index 000000000..1a1d8b8e9
--- /dev/null
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/EMFUtils.java
@@ -0,0 +1,361 @@
+/*******************************************************************************
+ * Copyright (c) 2024, 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.sysml.metamodel.helper;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+
+import org.eclipse.emf.common.notify.Adapter;
+import org.eclipse.emf.common.notify.Notifier;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature.Setting;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.resource.ResourceSet;
+import org.eclipse.emf.ecore.util.ECrossReferenceAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utils class to browse or edit EMF model.
+ *
+ * @author Arthur Daussy
+ */
+public class EMFUtils {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EMFUtils.class);
+
+ /**
+ * Gets the ancestor of given type starting from a specific element.
+ *
+ * @param object
+ * an {@link EObject}
+ * @param type
+ * expected type
+ * @param
+ * expected type
+ * @return a expected typed ancestor or null;
+ */
+ private static T getAncestor(Class type, EObject object) {
+ if (object == null) {
+ return null;
+ }
+ final T result;
+ if (type.isInstance(object)) {
+ result = (T) object;
+ } else {
+ result = getAncestor(type, object.eContainer());
+ }
+ return result;
+ }
+
+ /**
+ * Gets all Settings targeting the given source element with the given {@link EReference}.
+ *
+ * @param source
+ * the source element
+ * @param targetingFeature
+ * the feature that link the searched elements to the source
+ * @return a {@link Collection} of {@link Setting}
+ */
+ public static Collection getInverse(EObject source, EReference targetingFeature) {
+ return source.eAdapters().stream()
+ .filter(ECrossReferenceAdapter.class::isInstance)
+ .map(ECrossReferenceAdapter.class::cast)
+ .map(crossRef -> crossRef.getInverseReferences(source, targetingFeature, false))
+ .findFirst().orElseGet(() -> {
+ LOGGER.warn("Unable to find an ECrossReference on " + source);
+ return List.of();
+ });
+
+ }
+
+ /**
+ * Gets all Settings targeting the given source element.
+ *
+ * @param source
+ * the source element
+ * @return a {@link Collection} of {@link Setting}
+ */
+ public static Collection getInverse(EObject source) {
+ return source.eAdapters().stream()
+ .filter(ECrossReferenceAdapter.class::isInstance)
+ .map(ECrossReferenceAdapter.class::cast)
+ .map(crossRef -> crossRef.getInverseReferences(source))
+ .findFirst()
+ .orElseGet(() -> {
+ LOGGER.warn("Unable to find an ECrossReference on " + source);
+ return List.of();
+ });
+
+ }
+
+ /**
+ * Gets the first adapter of a given type installed on a Notifier.
+ *
+ * @param notifier
+ * notifier on which the adapter is installed
+ * @param adapterType
+ * the type of the desired adapter
+ * @param
+ * the type of the desired adapter
+ * @return an optional {@link Notifier}
+ */
+ public static Optional getAdapter(Notifier notifier, Class adapterType) {
+ if (notifier != null) {
+ return notifier.eAdapters().stream()
+ .filter(adapterType::isInstance)
+ .map(adapterType::cast)
+ .findFirst();
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Gets a stream composed from the object itself and all its content.
+ *
+ * @param o
+ * an {@link EObject}
+ * @return a stream
+ */
+ public static Stream eAllContentStreamWithSelf(EObject o) {
+ if (o == null) {
+ return Stream.empty();
+ }
+ return Stream.concat(Stream.of(o), StreamSupport
+ .stream(Spliterators.spliteratorUnknownSize(o.eAllContents(), Spliterator.NONNULL), false));
+ }
+
+ /**
+ * Gets a stream composed from the object itself and all its content.
+ *
+ * @param r
+ * a resource
+ * @return a stream
+ */
+ public static Stream eAllContentStreamWithSelf(Resource r) {
+ if (r == null) {
+ return Stream.empty();
+ }
+ return Stream.concat(Stream.of(r), StreamSupport
+ .stream(Spliterators.spliteratorUnknownSize(r.getAllContents(), Spliterator.NONNULL), false));
+ }
+
+ /**
+ * Gets a stream composed from the object itself and all its content.
+ *
+ * @param n
+ * a notifier
+ * @return a stream
+ */
+ public static Stream eAllContentStreamWithSelf(Notifier n) {
+ Stream result = Stream.empty();
+ if (n instanceof Resource resource) {
+ result = eAllContentStreamWithSelf(resource);
+ } else if (n instanceof EObject eObject) {
+ result = eAllContentStreamWithSelf(eObject).map(Notifier.class::cast);
+ } else if (n instanceof ResourceSet resourceSet) {
+ result = eAllContentStreamWithSelf(resourceSet);
+ }
+ return result;
+ }
+
+ /**
+ * Gets a stream composed from the object itself and all its content.
+ *
+ * @param rs
+ * a resource set
+ * @return a stream
+ */
+ public static Stream eAllContentStreamWithSelf(ResourceSet rs) {
+ if (rs == null) {
+ return Stream.empty();
+ }
+ return Stream.concat(Stream.of(rs), StreamSupport
+ .stream(Spliterators.spliteratorUnknownSize(rs.getAllContents(), Spliterator.NONNULL), false));
+ }
+
+ /**
+ * Gets all objects contained in the given notifier with the given type.
+ *
+ * If self if of the expected type then it belong to the returned stream
+ *
+ *
+ * @param self
+ * a {@link Notifier} (EObject, Resource or ResourceSet)
+ * @param type
+ * the type of the element in the returned stream
+ * @return a stream
+ * @param
+ * type of element in the returned stream
+ */
+ public static Stream allContainedObjectOfType(Notifier self, Class type) {
+ final Stream result;
+ if (self instanceof EObject eObject) {
+ result = eAllContentStreamWithSelf(eObject).filter(e -> type.isInstance(e)).map(e -> type.cast(e));
+ } else if (self instanceof Resource resource) {
+ result = eAllContentStreamWithSelf(resource).filter(e -> type.isInstance(e)).map(e -> type.cast(e));
+ } else if (self instanceof ResourceSet resourceSet) {
+ result = eAllContentStreamWithSelf(resourceSet).filter(e -> type.isInstance(e))
+ .map(e -> type.cast(e));
+ } else {
+ result = Stream.empty();
+ }
+ return result;
+ }
+
+ /**
+ * Returns {@code true} if {@code parent} is an ancestor of {@code eObject}.
+ *
+ * This method includes {@code eObject} as an ancestor of itself. This means that this method will always return
+ * {@code true} if {@code parent == eObject}.
+ *
+ *
+ * @param parent
+ * the parent EObject to check
+ * @param eObject
+ * the EObject to check
+ * @return {@code true} if {@code parent} is an ancestor of {@code eObject}
+ */
+ public static boolean isAncestor(EObject parent, EObject eObject) {
+ return !getAncestors(EObject.class, eObject, ancestor -> Objects.equals(ancestor, parent)).isEmpty();
+ }
+
+ /**
+ * Gets the first ancestor from the given object which match the predicated and has the expected type.
+ *
+ * @param
+ * the expected type
+ * @param type
+ * the expected type
+ * @param object
+ * the source object
+ * @param ancestorPredicate
+ * an optional {@link Predicate}
+ * @return an ancestor or null
+ */
+ public static List getAncestors(Class type, EObject object, Predicate ancestorPredicate) {
+ var current = object;
+ List results = new ArrayList<>();
+ while (current != null) {
+ if (type.isInstance(current) && (ancestorPredicate == null || ancestorPredicate.test(current))) {
+ results.add((T) current);
+ }
+ current = current.eContainer();
+ }
+ return results;
+ }
+
+ /**
+ * Gets the first ancestor containing the given object with the expected type. An optional predicate can be
+ * specified to add more constraint on the searched ancestor..
+ *
+ * @param
+ * the expected type of the ancestor
+ * @param type
+ * the expected type of the ancestor
+ * @param object
+ * the source object
+ * @param ancestorPredicate
+ * an optional predicate than can be used to add another constraint on the searched ancestor (set to
+ * null if the type constraint is enough)
+ * @return a matching ancestor or {@link Optional#empty()}
+ */
+ public static Optional getFirstAncestor(Class type, EObject object, Predicate ancestorPredicate) {
+ var current = object;
+ while (current != null) {
+ if (type.isInstance(current) && (ancestorPredicate == null || ancestorPredicate.test(current))) {
+ return Optional.of((T) current);
+ }
+ current = current.eContainer();
+ }
+ return Optional.empty();
+ }
+
+ /**
+ * Gets all the ancestors of the given object which have the expected type.
+ *
+ * @param
+ * the expected type
+ * @param type
+ * the expected type
+ * @param object
+ * the source object
+ * @return a list of ancestors
+ */
+ private static List getAncestors(Class type, EObject object) {
+ return getAncestors(type, object, null);
+ }
+
+ /**
+ * Gets the first common ancestor from both given objects with the expected type.
+ *
+ * @param
+ * the expected ancestor type
+ * @param expectedType
+ * the expected ancestor type
+ * @param e1
+ * the first {@link EObject}
+ * @param e2
+ * the last {@link EObject}
+ * @return an ancestor or null
+ */
+ public static T getLeastCommonContainer(Class expectedType, EObject e1, EObject e2) {
+
+ List e1Ancestors = getAncestors(expectedType, e1);
+ List e2CommonAncestors = getAncestors(expectedType, e2, container -> e1Ancestors.contains(container));
+ if (e2CommonAncestors.isEmpty()) {
+ return null;
+ } else {
+ return e2CommonAncestors.get(0);
+ }
+ }
+
+ /**
+ * Resolve all non derived references in the given {@link ResourceSet}.
+ *
+ * @param rs
+ * the given {@link ResourceSet}.
+ */
+ public static void resolveAllNonDerived(ResourceSet rs) {
+ List resources = rs.getResources();
+ for (int i = 0; i < resources.size(); i++) {
+ resolveAllNonDerived(resources.get(i));
+ }
+ }
+
+ private static void resolveAllNonDerived(Resource resource) {
+ resource.getContents().forEach(eObject -> resolveAllNonDerived(eObject));
+ }
+
+ private static void resolveAllNonDerived(EObject eObject) {
+ resolveNonDerivedCrossReferences(eObject);
+ for (Iterator i = eObject.eAllContents(); i.hasNext(); ) {
+ EObject childEObject = i.next();
+ resolveNonDerivedCrossReferences(childEObject);
+ }
+ }
+
+ private static void resolveNonDerivedCrossReferences(EObject eObject) {
+ eObject.eClass().getEAllReferences().stream().filter(e -> !e.isDerived() && !e.isContainment()).forEach(ref -> eObject.eGet(ref, true));
+ }
+}
diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/ImplicitSpecializationAccumulator.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/ImplicitSpecializationAccumulator.java
new file mode 100644
index 000000000..56a305f2a
--- /dev/null
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/ImplicitSpecializationAccumulator.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2025, 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.sysml.metamodel.helper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.syson.sysml.FeatureTyping;
+import org.eclipse.syson.sysml.Redefinition;
+import org.eclipse.syson.sysml.ReferenceSubsetting;
+import org.eclipse.syson.sysml.Specialization;
+import org.eclipse.syson.sysml.Subclassification;
+import org.eclipse.syson.sysml.Subsetting;
+
+/**
+ * Accumulator of {@link Specialization} that keeps track of the type of the added {@link Specialization}.
+ *
+ * @author Arthur Daussy
+ */
+public class ImplicitSpecializationAccumulator {
+
+ private boolean hasSubSetting;
+
+ private boolean hasRedefinition;
+
+ private boolean hasSubclassification;
+
+ private boolean hasFeatureTyping;
+
+ private final List accumulator = new ArrayList<>();
+
+ public ImplicitSpecializationAccumulator(boolean hasSubSetting, boolean hasRedefinition, boolean hasSubclassification, boolean hasFeatureTyping) {
+ super();
+ this.hasSubSetting = hasSubSetting;
+ this.hasRedefinition = hasRedefinition;
+ this.hasSubclassification = hasSubclassification;
+ this.hasFeatureTyping = hasFeatureTyping;
+ }
+
+ public static ImplicitSpecializationAccumulator fromExistingSpecialization(List specialization) {
+ boolean hasSubSetting = false;
+ boolean hasRedefinition = false;
+ boolean hasSubclassification = false;
+ boolean hasFeatureTyping = false;
+
+ for (Specialization s : specialization) {
+ if (!hasSubSetting && s instanceof Subsetting && !(s instanceof Redefinition) && !(s instanceof ReferenceSubsetting)) {
+ hasSubSetting = true;
+ }
+ if (!hasRedefinition && s instanceof Redefinition) {
+ hasRedefinition = true;
+ }
+ if (!hasSubclassification && s instanceof Subclassification) {
+ hasSubclassification = true;
+ }
+ if (!hasFeatureTyping && s instanceof FeatureTyping) {
+ hasFeatureTyping = true;
+ }
+ }
+ return new ImplicitSpecializationAccumulator(hasSubSetting, hasRedefinition, hasSubclassification, hasFeatureTyping);
+ }
+
+ public void addAll(List extends Specialization> specializations) {
+ for (Specialization s : specializations) {
+ this.add(s);
+ }
+ }
+
+ public boolean hasSubSetting() {
+ return this.hasSubSetting;
+ }
+
+ public boolean hasRedefinition() {
+ return this.hasRedefinition;
+ }
+
+ public boolean hasSubclassification() {
+ return this.hasSubclassification;
+ }
+
+ public boolean hasFeatureTyping() {
+ return this.hasFeatureTyping;
+ }
+
+ public List getSpecializations() {
+ return this.accumulator;
+ }
+
+ public void add(Specialization s) {
+ if (!this.hasSubSetting && s instanceof Subsetting && !(s instanceof Redefinition) && !(s instanceof ReferenceSubsetting)) {
+ this.hasSubSetting = true;
+ }
+ if (!this.hasRedefinition && s instanceof Redefinition) {
+ this.hasRedefinition = true;
+ }
+ if (!this.hasSubclassification && s instanceof Subclassification) {
+ this.hasSubclassification = true;
+ }
+ if (!this.hasFeatureTyping && s instanceof FeatureTyping) {
+ this.hasFeatureTyping = true;
+ }
+ this.accumulator.add(s);
+ }
+
+}
diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/ImplicitSpecializationSwitch.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/ImplicitSpecializationSwitch.java
new file mode 100644
index 000000000..9b320b46d
--- /dev/null
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/ImplicitSpecializationSwitch.java
@@ -0,0 +1,1652 @@
+/*******************************************************************************
+ * Copyright (c) 2024, 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.sysml.metamodel.helper;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Optional;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.syson.sysml.AcceptActionUsage;
+import org.eclipse.syson.sysml.ActionDefinition;
+import org.eclipse.syson.sysml.ActionUsage;
+import org.eclipse.syson.sysml.ActorMembership;
+import org.eclipse.syson.sysml.AllocationDefinition;
+import org.eclipse.syson.sysml.AllocationUsage;
+import org.eclipse.syson.sysml.AnalysisCaseDefinition;
+import org.eclipse.syson.sysml.AnalysisCaseUsage;
+import org.eclipse.syson.sysml.AssertConstraintUsage;
+import org.eclipse.syson.sysml.AssignmentActionUsage;
+import org.eclipse.syson.sysml.AttributeUsage;
+import org.eclipse.syson.sysml.Behavior;
+import org.eclipse.syson.sysml.CalculationDefinition;
+import org.eclipse.syson.sysml.CalculationUsage;
+import org.eclipse.syson.sysml.CaseDefinition;
+import org.eclipse.syson.sysml.CaseUsage;
+import org.eclipse.syson.sysml.Classifier;
+import org.eclipse.syson.sysml.ConcernDefinition;
+import org.eclipse.syson.sysml.ConcernUsage;
+import org.eclipse.syson.sysml.ConnectionDefinition;
+import org.eclipse.syson.sysml.ConnectionUsage;
+import org.eclipse.syson.sysml.Connector;
+import org.eclipse.syson.sysml.ConstraintDefinition;
+import org.eclipse.syson.sysml.ConstraintUsage;
+import org.eclipse.syson.sysml.ControlNode;
+import org.eclipse.syson.sysml.DecisionNode;
+import org.eclipse.syson.sysml.Definition;
+import org.eclipse.syson.sysml.Element;
+import org.eclipse.syson.sysml.EndFeatureMembership;
+import org.eclipse.syson.sysml.EventOccurrenceUsage;
+import org.eclipse.syson.sysml.ExhibitStateUsage;
+import org.eclipse.syson.sysml.Expression;
+import org.eclipse.syson.sysml.Feature;
+import org.eclipse.syson.sysml.FeatureTyping;
+import org.eclipse.syson.sysml.FeatureValue;
+import org.eclipse.syson.sysml.FlowDefinition;
+import org.eclipse.syson.sysml.FlowUsage;
+import org.eclipse.syson.sysml.ForLoopActionUsage;
+import org.eclipse.syson.sysml.ForkNode;
+import org.eclipse.syson.sysml.FramedConcernMembership;
+import org.eclipse.syson.sysml.IfActionUsage;
+import org.eclipse.syson.sysml.IncludeUseCaseUsage;
+import org.eclipse.syson.sysml.InterfaceDefinition;
+import org.eclipse.syson.sysml.InterfaceUsage;
+import org.eclipse.syson.sysml.ItemDefinition;
+import org.eclipse.syson.sysml.ItemUsage;
+import org.eclipse.syson.sysml.JoinNode;
+import org.eclipse.syson.sysml.Membership;
+import org.eclipse.syson.sysml.MergeNode;
+import org.eclipse.syson.sysml.MetadataAccessExpression;
+import org.eclipse.syson.sysml.MetadataDefinition;
+import org.eclipse.syson.sysml.MetadataUsage;
+import org.eclipse.syson.sysml.Multiplicity;
+import org.eclipse.syson.sysml.Namespace;
+import org.eclipse.syson.sysml.OccurrenceDefinition;
+import org.eclipse.syson.sysml.OccurrenceUsage;
+import org.eclipse.syson.sysml.OperatorExpression;
+import org.eclipse.syson.sysml.PartDefinition;
+import org.eclipse.syson.sysml.PartUsage;
+import org.eclipse.syson.sysml.PerformActionUsage;
+import org.eclipse.syson.sysml.PortDefinition;
+import org.eclipse.syson.sysml.PortUsage;
+import org.eclipse.syson.sysml.PortionKind;
+import org.eclipse.syson.sysml.Redefinition;
+import org.eclipse.syson.sysml.ReferenceSubsetting;
+import org.eclipse.syson.sysml.ReferenceUsage;
+import org.eclipse.syson.sysml.Relationship;
+import org.eclipse.syson.sysml.RenderingDefinition;
+import org.eclipse.syson.sysml.RenderingUsage;
+import org.eclipse.syson.sysml.RequirementConstraintKind;
+import org.eclipse.syson.sysml.RequirementConstraintMembership;
+import org.eclipse.syson.sysml.RequirementDefinition;
+import org.eclipse.syson.sysml.RequirementUsage;
+import org.eclipse.syson.sysml.RequirementVerificationMembership;
+import org.eclipse.syson.sysml.SendActionUsage;
+import org.eclipse.syson.sysml.Specialization;
+import org.eclipse.syson.sysml.StakeholderMembership;
+import org.eclipse.syson.sysml.StateDefinition;
+import org.eclipse.syson.sysml.StateSubactionKind;
+import org.eclipse.syson.sysml.StateSubactionMembership;
+import org.eclipse.syson.sysml.StateUsage;
+import org.eclipse.syson.sysml.Step;
+import org.eclipse.syson.sysml.Subclassification;
+import org.eclipse.syson.sysml.Subsetting;
+import org.eclipse.syson.sysml.SuccessionAsUsage;
+import org.eclipse.syson.sysml.SuccessionFlowUsage;
+import org.eclipse.syson.sysml.SysmlFactory;
+import org.eclipse.syson.sysml.TransitionUsage;
+import org.eclipse.syson.sysml.TriggerInvocationExpression;
+import org.eclipse.syson.sysml.TriggerKind;
+import org.eclipse.syson.sysml.Type;
+import org.eclipse.syson.sysml.Usage;
+import org.eclipse.syson.sysml.UseCaseDefinition;
+import org.eclipse.syson.sysml.UseCaseUsage;
+import org.eclipse.syson.sysml.VerificationCaseDefinition;
+import org.eclipse.syson.sysml.VerificationCaseUsage;
+import org.eclipse.syson.sysml.ViewDefinition;
+import org.eclipse.syson.sysml.ViewRenderingMembership;
+import org.eclipse.syson.sysml.ViewUsage;
+import org.eclipse.syson.sysml.ViewpointDefinition;
+import org.eclipse.syson.sysml.ViewpointUsage;
+import org.eclipse.syson.sysml.WhileLoopActionUsage;
+import org.eclipse.syson.sysml.util.ILibraryNamespaceProvider;
+import org.eclipse.syson.sysml.util.SysmlSwitch;
+
+/**
+ * Switch allowing to compute implicit specializations for a given Element.
+ *
+ * @author arichard
+ */
+public class ImplicitSpecializationSwitch extends SysmlSwitch> {
+
+ private final ImplicitSpecializationAccumulator implicitSpecializations;
+
+ private final ILibraryNamespaceProvider namespaceProvider;
+
+ public ImplicitSpecializationSwitch(List existingSpecializations, ILibraryNamespaceProvider namespaceProvider) {
+ this.namespaceProvider = namespaceProvider;
+ this.implicitSpecializations = ImplicitSpecializationAccumulator.fromExistingSpecialization(existingSpecializations);
+ }
+
+ @Override
+ public List doSwitch(EObject eObject) {
+ super.doSwitch(eObject);
+ return this.implicitSpecializations.getSpecializations();
+ }
+
+ @Override
+ public List caseAcceptActionUsage(AcceptActionUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ String implicitElement = null;
+ if (object.isIsComposite() && object.isSubactionUsage() && !object.isTriggerAction()) {
+ // A composite AcceptActionUsage that is a subaction usage, but is not the triggerAction of a
+ // TransitionUsage, must directly or indirectly specialize the
+ // ActionUsageActions::Action::acceptSubactions from the Systems Model Library.
+ implicitElement = "Actions::Action::acceptSubactions";
+ } else if (!object.isTriggerAction()) {
+ // An AcceptActionUsage that is not the triggerAction of a TransitionUsage must directly or indirectly
+ // specialize the ActionUsage Actions::acceptActions from the Systems Model Library.
+ implicitElement = "Actions::acceptActions";
+ } else if (object.isTriggerAction()) {
+ // An AcceptActionUsage that is the triggerAction of TransitionUsage must directly or indirectly
+ // specialize the ActionUsage Actions::TransitionAction::accepter from the Systems Model Library.
+ implicitElement = "Actions::TransitionAction::accepter";
+ }
+ if (implicitElement != null) {
+ var implicitSubsetting = this.implicitSubsetting(object, implicitElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseReferenceUsage(ReferenceUsage referenceUsage) {
+
+ Type owningType = referenceUsage.getOwningType();
+ if (owningType instanceof SuccessionAsUsage successionAsUsage) {
+ this.implicitSpecializations.addAll(this.handleReferenceUsageInSuccessionAsUsage(referenceUsage, successionAsUsage));
+ }
+
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseActionDefinition(ActionDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Actions::Action");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseActionUsage(ActionUsage object) {
+ var implicitSubsettingElement = "Actions::actions";
+ // A composite ActionUsage whose owningType is PartDefinition or PartUsage must directly or indirectly
+ // specialize the ActionUsage Parts::Part::ownedActions from the Systems Model Library.
+ var owningType = object.getOwningType();
+ var owningFeatureMembership = object.getOwningFeatureMembership();
+ if (owningFeatureMembership instanceof StateSubactionMembership ssm && !this.implicitSpecializations.hasRedefinition()) {
+ // An ActionUsage that is the entry, do, or exit Action of a StateDefinition or StateUsage must redefine the
+ // entryAction, doAction, or exitAction feature, respectively, of the StateDefinition States::StateAction
+ // from the Systems Model Library.
+ var implicitRedefinitionElement = "States::StateAction::exitAction";
+ var kind = ssm.getKind();
+ if (kind == StateSubactionKind.ENTRY) {
+ implicitRedefinitionElement = "States::StateAction::entryAction";
+ } else if (kind == StateSubactionKind.DO) {
+ implicitRedefinitionElement = "States::StateAction::doAction";
+ }
+ var implicitRedefinition = this.implicitRedefinition(object, implicitRedefinitionElement);
+ if (implicitRedefinition != null) {
+ this.implicitSpecializations.add(implicitRedefinition);
+ }
+ } else if (object.isIsComposite() && (owningType instanceof PartDefinition || owningType instanceof PartUsage)) {
+ implicitSubsettingElement = "Parts::Part::ownedActions";
+ } else if (object.isIsComposite() && object.isSubactionUsage()) {
+ // A composite ActionUsage that is a subaction usage must directly or indirectly specialize the ActionUsage
+ // Actions::Action::subactions from the Systems Model Library.
+ implicitSubsettingElement = "Actions::Action::subactions";
+ }
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseAllocationDefinition(AllocationDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Allocations::Allocation");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseAllocationUsage(AllocationUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Allocations::allocations");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseAnalysisCaseDefinition(AnalysisCaseDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "AnalysisCases::AnalysisCase");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseAnalysisCaseUsage(AnalysisCaseUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "AnalysisCases::analysisCases";
+ // A composite AnalysisCaseUsage whose owningType is an AnalysisCaseDefinition or AnalysisCaseUsage must
+ // specialize the AnalysisCaseUsage AnalysisCases::AnalysisCase::subAnalysisCases from the Systems Model
+ // Library.
+ var owningType = object.getOwningType();
+ if (owningType instanceof AnalysisCaseDefinition || owningType instanceof AnalysisCaseUsage) {
+ implicitSubsettingElement = "AnalysisCases::AnalysisCase::subAnalysisCases";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseAssertConstraintUsage(AssertConstraintUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Constraints::assertedConstraints";
+ // If a AssertConstraintUsage is negated, then it must directly or indirectly specialize the ConstraintUsage
+ // Constraints::negatedConstraints. Otherwise, it must directly or indirectly specialize the ConstraintUsage
+ // Constraints::assertedConstraints.
+ if (object.isIsNegated()) {
+ implicitSubsettingElement = "Constraints::negatedConstraints";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseAssignmentActionUsage(AssignmentActionUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Actions::assignmentActions";
+ // A composite AssignmentActionUsage that is a subaction usage must directly or indirectly specialize the
+ // ActionUsage Actions::Action::assignments from the Systems Model Library.
+ if (object.isIsComposite() && object.isSubactionUsage()) {
+ implicitSubsettingElement = "Actions::Action::assignments";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseAttributeUsage(AttributeUsage object) {
+ if (this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Base::dataValues");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseCalculationDefinition(CalculationDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Calculations::Calculation");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseCalculationUsage(CalculationUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Calculations::calculations";
+ var owningType = object.getOwningType();
+ if (owningType instanceof CalculationDefinition || owningType instanceof CalculationUsage) {
+ implicitSubsettingElement = "Calculations::Calculation::subcalculations";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseCaseDefinition(CaseDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Cases::Case");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseCaseUsage(CaseUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Cases::cases";
+ // A composite CaseUsage whose owningType is a CaseDefinition or CaseUsage must directly or indirectly
+ // specialize the CaseUsage Cases::Case::subcases.
+ var owningType = object.getOwningType();
+ if (owningType instanceof CaseDefinition || owningType instanceof CaseUsage) {
+ implicitSubsettingElement = "Cases::Case::subcases";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseConcernDefinition(ConcernDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Requirements::ConcernCheck");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseConcernUsage(ConcernUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Requirements::concernChecks";
+ // If a ConcernUsage is owned via a FramedConcernMembership, then it must directly or indirectly specialize
+ // the ConcernUsage Requirements::RequirementCheck::concerns from the Systems Model Library.
+ var owningFeatureMembership = object.getOwningFeatureMembership();
+ if (owningFeatureMembership instanceof FramedConcernMembership) {
+ implicitSubsettingElement = "Requirements::RequirementCheck::concerns";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseConnectionDefinition(ConnectionDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassificationElement = "Connections::Connection";
+ // A binary ConnectionDefinition must directly or indirectly specialize the ConnectionDefinition
+ // Connections::BinaryConnection from the Systems Model Library.
+ if (object.getOwnedEndFeature().size() == 2) {
+ implicitSubclassificationElement = "Connections::BinaryConnection";
+ }
+ var implicitSubclassification = this.implicitSubclassification(object, implicitSubclassificationElement);
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseConnectionUsage(ConnectionUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Connections::connections";
+ // A binary ConnectionUsage must directly or indirectly specialize the ConnectionUsage
+ // Connections::binaryConnections from the Systems Model Library.
+ if (object.getOwnedEndFeature().size() == 2) {
+ implicitSubsettingElement = "Connections::binaryConnections";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseConstraintDefinition(ConstraintDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Constraints::ConstraintCheck");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseConstraintUsage(ConstraintUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Constraints::constraintChecks";
+ // A ConstraintUsage whose owningType is an ItemDefinition or ItemUsage must directly or indirectly
+ // specialize the ConstraintUsage Items::Item::checkedConstraints.
+ var owningType = object.getOwningType();
+ if (owningType instanceof ItemDefinition || owningType instanceof ItemUsage) {
+ implicitSubsettingElement = "Items::Item::checkedConstraints";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ // A ConstraintUsage whose owningFeatureMembership is a RequirementConstraintMembership must directly or
+ // indirectly specialize on the ConstraintUsages assumptions or constraints from the onstraintDefinition
+ // Requirements::RequirementCheck in the Systems Model Library, depending on whether the kind of the
+ // RequirementConstraintMembership is assumption or requirement, respectively.
+ var owningFeatureMembership = object.getOwningFeatureMembership();
+ if (owningFeatureMembership instanceof RequirementConstraintMembership rcm) {
+ var kind = rcm.getKind();
+ if (kind == RequirementConstraintKind.ASSUMPTION) {
+ implicitSubsettingElement = "Requirements::RequirementCheck::assumptions";
+ } else {
+ implicitSubsettingElement = "Requirements::RequirementCheck::constraints";
+ }
+ implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseControlNode(ControlNode object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Action::Action::controls");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseDecisionNode(DecisionNode object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Actions::Action::decisions");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseEventOccurrenceUsage(EventOccurrenceUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ // If an EventOccurrenceUsage has an owningType that is an OccurrenceDefinition or OccurrenceUsage, then it
+ // must directly or indirectly specialize the Feature Occurrences::Occurrence::timeEnclosedOccurrences.
+ var owningType = object.getOwningType();
+ if (owningType instanceof OccurrenceDefinition || owningType instanceof OccurrenceUsage) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Occurrences::Occurrence::timeEnclosedOccurrences");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseExhibitStateUsage(ExhibitStateUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ // If an ExhibitStateUsage has an owningType that is a PartDefinition or PartUsage, then it must directly or
+ // indirectly specialize the StateUsage Parts::Part::exhibitedStates.
+ var owningType = object.getOwningType();
+ if (owningType instanceof PartDefinition || owningType instanceof PartUsage) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Parts::Part::exhibitedStates");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseFeature(Feature object) {
+ if (!this.implicitSpecializations.hasRedefinition()) {
+ this.implicitSpecializations.addAll(this.handleImplicitParameterRedefinition(object));
+ }
+ // The specification states that "If the Feature has chainingFeatures, then the union also includes the types of
+ // the last chainingFeature".We need to implement this here in order to make inherited Feature resolvable.
+ // If we only implement this in "getType" of the Feature implementation the general mechanism of name resolution
+ // that relies on "getMembership" would fail.
+ // Normally the implicit typing would be present in the model (either with an implicit inheritance
+ // or implicit typing). But since in SysON we choose to add those "implicit" element virtually when the derived
+ // feature are called, we needed to find a place where this 'implicit' typing would impact both Feature.getType
+ // and Namespace.getMembership. implementations.
+
+ EList chainingFeature = object.getChainingFeature();
+ if (!chainingFeature.isEmpty()) {
+ Feature lastFeature = chainingFeature.get(chainingFeature.size() - 1);
+ for (Type type : lastFeature.getType()) {
+ FeatureTyping featureTyping = SysmlFactory.eINSTANCE.createFeatureTyping();
+ this.implicitSpecializations.add(featureTyping);
+ featureTyping.setType(type);
+ featureTyping.setTypedFeature(object);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseFlowDefinition(FlowDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Connections::MessageConnection");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseFlowUsage(FlowUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Connections::flowConnections";
+ // If a FlowUsage has no ownedEndFeatures, then it must directly or indirectly specialize the base
+ // FlowUsage Connections::messageConnections from the Systems Library model.
+ if (object.getOwnedEndFeature().isEmpty()) {
+ implicitSubsettingElement = "Connections::messageConnections";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseForkNode(ForkNode object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Actions::Action::forks");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseForLoopActionUsage(ForLoopActionUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Actions::forLoopActions";
+ // A composite ForLoopActionUsage that is a subaction usage must directly or indirectly specialize the
+ // ActionUsage Actions::Action::forLoops from the Systems Model Library.
+ if (object.isIsComposite() && object.isSubactionUsage()) {
+ implicitSubsettingElement = "Actions::Action::forLoops";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseIfActionUsage(IfActionUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Actions::ifThenActions";
+ if (object.getElseAction() != null) {
+ // A IfActionUsage must directly or indirectly specialize the ActionUsage Actions::ifThenActions from
+ // the Systems Model Library. If it has an elseAction, then it must directly or indirectly specialize
+ // Actions::ifThenElseActions
+ implicitSubsettingElement = "Actions::ifThenElseActions";
+ } else if (object.isIsComposite() && object.isSubactionUsage()) {
+ // A composite IfActionUsage that is a subaction usage must directly or indirectly specialize the
+ // ActionUsage Actions::Action::ifSubactions from the Systems Model Library.
+ implicitSubsettingElement = "Actions::Action::ifSubactions";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseIncludeUseCaseUsage(IncludeUseCaseUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ // A IncludeUseCaseUsage whose owningType is a UseCaseDefinition or UseCaseUsage must directly or indirectly
+ // specialize the UseCaseUsage UseCases::UseCase::includedUseCases from the Systems Model Library.
+ var owningType = object.getOwningType();
+ if (owningType instanceof UseCaseDefinition || owningType instanceof UseCaseUsage) {
+ var implicitSubsetting = this.implicitSubsetting(object, "UseCases::UseCase::includedUseCases");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseInterfaceDefinition(InterfaceDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassificationElement = "Interfaces::Interface";
+ // A binary InterfaceDefinition must directly or indirectly specialize the InterfaceDefinition
+ // Interfaces::BinaryInterface from the Systems Model Library.
+ if (object.getOwnedEndFeature().size() == 2) {
+ implicitSubclassificationElement = "Interfaces::BinaryInterface";
+ }
+ var implicitSubclassification = this.implicitSubclassification(object, implicitSubclassificationElement);
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseInterfaceUsage(InterfaceUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Interfaces::interfaces";
+ // A binary InterfaceUsage must directly or indirectly specialize the InterfaceUsage
+ // Interfaces::binaryInterfaces from the Systems Model Library.
+ if (object.getOwnedEndFeature().size() == 2) {
+ implicitSubsettingElement = "Interfaces::binaryInterfaces";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseItemDefinition(ItemDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Items::Item");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseItemUsage(ItemUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Items::items");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseJoinNode(JoinNode object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Actions::Action::join");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseMergeNode(MergeNode object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Actions::Action::merges");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseMetadataDefinition(MetadataDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Metadata::MetadataItem");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseMetadataUsage(MetadataUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Metadata::metadataItems");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseMultiplicity(Multiplicity object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Base::naturals");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseOccurrenceDefinition(OccurrenceDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Occurrences::Occurrence");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseOccurrenceUsage(OccurrenceUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ if (PortionKind.TIMESLICE.equals(object.getPortionKind())) {
+ var implicitTimeSliceSubsetting = this.implicitSubsetting(object, "Occurrences::Occurrence::timeSlices");
+ if (implicitTimeSliceSubsetting != null) {
+ this.implicitSpecializations.add(implicitTimeSliceSubsetting);
+ }
+ } else if (PortionKind.SNAPSHOT.equals(object.getPortionKind())) {
+ var implicitSnapshotSubsetting = this.implicitSubsetting(object, "Occurrences::Occurrence::snapshots");
+ if (implicitSnapshotSubsetting != null) {
+ this.implicitSpecializations.add(implicitSnapshotSubsetting);
+ }
+ } else {
+ var implicitSubsetting = this.implicitSubsetting(object, "Occurrences::occurrences");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ } else {
+ if (PortionKind.TIMESLICE.equals(object.getPortionKind())) {
+ // If the occurrence has subsettings and is a time slice, then we should ensure one of the subsetting specializes 'Occurrences::Occurrence::timeSlices'
+ object.getOwnedRelationship().stream()
+ .filter(Subsetting.class::isInstance)
+ .map(Subsetting.class::cast)
+ .map(Subsetting::getSubsettedFeature)
+ .filter(feature -> !feature.specializesFromLibrary("Occurrences::Occurrence::timeSlices"))
+ .findFirst()
+ .ifPresent(feature -> {
+ var implicitTimeSliceSubsetting = this.implicitSubsetting(object, "Occurrences::Occurrence::timeSlices");
+ if (implicitTimeSliceSubsetting != null) {
+ this.implicitSpecializations.add(implicitTimeSliceSubsetting);
+ }
+ });
+ } else if (PortionKind.SNAPSHOT.equals(object.getPortionKind())) {
+ // If the occurrence has subsettings and is a snapshot, then we should ensure one of the subsetting specializes 'Occurrences::Occurrence::snapshots'
+ object.getOwnedRelationship().stream()
+ .filter(Subsetting.class::isInstance)
+ .map(Subsetting.class::cast)
+ .map(Subsetting::getSubsettedFeature)
+ .filter(feature -> !feature.specializesFromLibrary("Occurrences::Occurrence::snapshots"))
+ .findFirst()
+ .ifPresent(feature -> {
+ var implicitTimeSliceSubsetting = this.implicitSubsetting(object, "Occurrences::Occurrence::snapshots");
+ if (implicitTimeSliceSubsetting != null) {
+ this.implicitSpecializations.add(implicitTimeSliceSubsetting);
+ }
+ });
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List casePartDefinition(PartDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Parts::Part");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List casePartUsage(PartUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Parts::parts";
+ var owningType = object.getOwningType();
+ if (object.getOwningFeatureMembership() instanceof ActorMembership) {
+ // If a PartUsage is owned via an ActorMembership, then it must directly or indirectly specialize either
+ // Requirements::RequirementCheck::actors (if its owningType is a RequirementDefinition or
+ // RequirementUsage or Cases::Case::actors (otherwise).
+ if (owningType instanceof RequirementDefinition || owningType instanceof RequirementUsage) {
+ implicitSubsettingElement = "Requirements::RequirementCheck::actors";
+ } else {
+ implicitSubsettingElement = "Cases::Case::actors";
+ }
+ } else if (object.getOwningFeatureMembership() instanceof StakeholderMembership) {
+ // If a PartUsage is owned via a StakeholderMembership, then it must directly or indirectly specialize
+ // either Requirements::RequirementCheck::stakeholders.
+ implicitSubsettingElement = "Requirements::RequirementCheck::stakeholders";
+ } else if (object.isIsComposite() && (owningType instanceof ItemDefinition || owningType instanceof ItemUsage)) {
+ // A composite PartUsage whose owningType is a ItemDefinition or ItemUsage must directly or indirectly
+ // specialize the PartUsage Items::Item::subparts from the Systems Model Library.
+ implicitSubsettingElement = "Items::Item::subparts";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List casePerformActionUsage(PerformActionUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ // If a PerformActionUsage has an owningType that is a PartDefinition or PartUsage, then it must directly or
+ // indirectly specialize the ActionUsage Parts::Part::performedActions.
+ var owningType = object.getOwningType();
+ if (owningType instanceof PartDefinition || owningType instanceof PartUsage) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Parts::Part::performedActions");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List casePortDefinition(PortDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Ports::Port");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List casePortUsage(PortUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Ports::ports";
+ // A composite PortUsage with an owningType that is a PortDefinition or PortUsage must directly or
+ // indirectly specialize the PortUsage Ports::Port::subports from the Systems Model Library.
+ var owningType = object.getOwningType();
+ if (object.isIsComposite() && (owningType instanceof PortDefinition || owningType instanceof PortUsage)) {
+ implicitSubsettingElement = "Ports::Port::subports";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseRenderingDefinition(RenderingDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Views::Rendering");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseRenderingUsage(RenderingUsage object) {
+ var owningFeatureMembership = object.getOwningFeatureMembership();
+ if (owningFeatureMembership instanceof ViewRenderingMembership && !(this.implicitSpecializations.hasRedefinition())) {
+ // A RenderingUsage whose owningFeatureMembership is a ViewRenderingMembership must redefine the
+ // RenderingUsage Views::View::viewRendering
+ var implicitRedefinitionElement = "Views::View::viewRendering";
+ var implicitRedefinition = this.implicitRedefinition(object, implicitRedefinitionElement);
+ if (implicitRedefinition != null) {
+ this.implicitSpecializations.add(implicitRedefinition);
+ }
+ }
+ if (this.implicitSpecializations.getSpecializations().isEmpty()) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Views::renderings";
+ var owningType = object.getOwningType();
+ if (object.isIsComposite() && (owningType instanceof RenderingDefinition || owningType instanceof RenderingUsage)) {
+ // A RenderingUsage whose owningType is a RenderingDefinition or RenderingUsage must directly or
+ // indirectly specialize the RenderingUsage Views::Rendering::subrenderings from the Systems Model
+ // Library.
+ implicitSubsettingElement = "Views::Rendering::subrenderings";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseRequirementDefinition(RequirementDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Requirements::RequirementCheck");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseRequirementUsage(RequirementUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Requirements::requirementChecks";
+ var owningType = object.getOwningType();
+ var owningFeatureMembership = object.getOwningFeatureMembership();
+ if (object.isIsComposite() && (owningType instanceof RequirementDefinition || owningType instanceof RequirementUsage)) {
+ // A composite RequirementUsage whose owningType is a RequirementDefinition or RequirementUsage must
+ // directly or indirectly specialize the RequirementUsage
+ // Requirements::RequirementCheck::subrequirements from the Systems Model Library.
+ implicitSubsettingElement = "Requirements::RequirementCheck::subrequirements";
+ } else if (owningFeatureMembership instanceof RequirementVerificationMembership) {
+ // RequirementUsage whose owningFeatureMembership is a RequirementVerificationMembership must directly
+ // or indirectly specialize the RequirementUsage
+ // VerificationCases::VerificationCase::obj::requirementVerifications.
+ implicitSubsettingElement = "VerificationCases::VerificationCase::obj::requirementVerifications";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseSendActionUsage(SendActionUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Actions::sendActions";
+ // A composite SendActionUsage that is a subaction must directly or indirectly specialize the ActionUsage
+ // Actions::Action::sendSubactions from the Systems Model Library.
+ if (object.isIsComposite() && object.isSubactionUsage()) {
+ implicitSubsettingElement = "Actions::Action::acceptSubactions";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseStateDefinition(StateDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "States::StateAction");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseStateUsage(StateUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "States::stateActions";
+ // A StateUsage that is a substate usage with a non-parallel owning StateDefinition or StateUsage must
+ // directly or indirectly specialize the StateUsage States::StateAction::exclusiveStates from the Systems
+ // Model Library.
+ if (object.isSubstateUsage(false)) {
+ implicitSubsettingElement = "States::StateAction::exclusiveStates";
+ } else if (object.isSubstateUsage(true)) {
+ implicitSubsettingElement = "States::StateAction::substates";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseSuccessionFlowUsage(SuccessionFlowUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsetting = this.implicitSubsetting(object, "Flows::successionFlows");
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseTransitionUsage(TransitionUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Actions::transitionActions";
+ // A composite TransitionUsage whose owningType is a ActionDefinition or ActionUsage, but not a
+ // StateDefinition or StateUsage, must directly or indirectly specialize the ActionUsage
+ // Actions::Action::decisionTransitions from the Systems Model Library. A composite TransitionUsage whose
+ // owningType is a StateDefinition or StateUsage must directly or indirectly specialize the ActionUsage
+ // States::State::stateTransitions from the Systems Model Library.
+ var owningType = object.getOwningType();
+ if (object.isIsComposite() && (owningType instanceof ActionDefinition || owningType instanceof ActionUsage)) {
+ if (!(owningType instanceof StateDefinition || owningType instanceof StateUsage)) {
+ implicitSubsettingElement = "Actions::Action::decisionTransitions";
+ } else {
+ implicitSubsettingElement = "States::State::stateTransitions";
+ }
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseTriggerInvocationExpression(TriggerInvocationExpression object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ // A TriggerInvocationExpression must directly or indirectly specialize one of the Functions TriggerWhen,
+ // TriggerAt or TriggerAfter, from the Kernel Semantic Library Triggers package, depending on whether its
+ // kind is when, at or after, respectively.
+ var implicitSubsettingElement = "Triggers::TriggerAfter";
+ if (object.getKind() == TriggerKind.WHEN) {
+ implicitSubsettingElement = "Triggers::TriggerWhen";
+ } else if (object.getKind() == TriggerKind.AT) {
+ implicitSubsettingElement = "Triggers::TriggerAt";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseType(Type object) {
+ // Describe limitation with use of external metadata
+ List metadataUsage = object.getOwnedMembership().stream()
+ .map(Membership::getMemberElement)
+ .filter(MetadataUsage.class::isInstance)
+ .map(MetadataUsage.class::cast)
+ .toList();
+
+ // Avoid any computation if no metadata usage is defined
+ if (!metadataUsage.isEmpty()) {
+ /*
+ * This code gets the MetadaUsage contained inside the object.
+ */
+ Type semanticMedatadaMetaclass = this.namespaceProvider.getNamespaceFromLibrary("Metaobjects::SemanticMetadata", Type.class);
+ List baseTypes = metadataUsage.stream()
+ .filter(MetadataUsage::isSemantic)
+ .flatMap(mu -> this.getBaseTypes(mu).stream())
+ .toList();
+
+ boolean isAnnotatedTypeAUsage = object instanceof Usage;
+ boolean isAnnotatedTypeADefinition = object instanceof Definition;
+
+ for (Type baseType : baseTypes) {
+ if (isAnnotatedTypeADefinition) {
+ Definition annotatedDefinition = (Definition) object;
+ if (baseType instanceof Classifier baseClassifier) {
+ this.implicitSpecializations.add(this.implicitSubclassification(annotatedDefinition, baseClassifier));
+ } else if (baseType instanceof Feature baseFeature) {
+ baseFeature.getType().stream()
+ .filter(Classifier.class::isInstance)
+ .map(Classifier.class::cast)
+ .forEach(baseClassifier -> {
+ this.implicitSpecializations.add(this.implicitSubclassification(annotatedDefinition, baseClassifier));
+ });
+ }
+ } else if (isAnnotatedTypeAUsage) {
+ Usage annotatedUsage = (Usage) object;
+ if (baseType instanceof Feature basefeature) {
+ this.implicitSpecializations.add(this.implicitReferenceSubsetting(annotatedUsage, basefeature));
+ }
+ }
+ }
+ }
+
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseUseCaseDefinition(UseCaseDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "UseCases::UseCase");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseUseCaseUsage(UseCaseUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "UseCases::useCases";
+ // A composite UseCaseUsage whose owningType is a UseCaseDefinition or UseCaseUsage must specialize the
+ // UseCaseUsage UseCases::UseCase::subUseCases from the Systems Model Library.
+ var owningType = object.getOwningType();
+ if (object.isIsComposite() && (owningType instanceof UseCaseDefinition || owningType instanceof UseCaseUsage)) {
+ implicitSubsettingElement = "UseCases::UseCase::subUseCases";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseVerificationCaseDefinition(VerificationCaseDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "VerificationCases::VerificationCase");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseVerificationCaseUsage(VerificationCaseUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "VerificationCases::verificationCases";
+ // If it is composite and owned by a VerificationCaseDefinition or VerificationCaseUsage, then it must
+ // specialize VerificationCaseUsage VerificationCases::VerificationCase::subVerificationCases.
+ var owningType = object.getOwningType();
+ if (object.isIsComposite() && (owningType instanceof VerificationCaseDefinition || owningType instanceof VerificationCaseUsage)) {
+ implicitSubsettingElement = "VerificationCases::VerificationCase::subVerificationCases";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseViewDefinition(ViewDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Views::View");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseViewpointDefinition(ViewpointDefinition object) {
+ if (!this.implicitSpecializations.hasSubclassification()) {
+ var implicitSubclassification = this.implicitSubclassification(object, "Views::Viewpoint");
+ if (implicitSubclassification != null) {
+ this.implicitSpecializations.add(implicitSubclassification);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseViewpointUsage(ViewpointUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Views::viewpoints";
+ // A composite ViewpointUsage whose owningType is a ViewDefinition or ViewUsage must directly or indirectly
+ // specialize the ViewpointUsage Views::View::viewpointSatisfactions from the Systems Model Library.
+ var owningType = object.getOwningType();
+ if (object.isIsComposite() && (owningType instanceof ViewDefinition || owningType instanceof ViewUsage)) {
+ implicitSubsettingElement = "Views::View::viewpointSatisfactions";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseViewUsage(ViewUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Views::views";
+ // A ViewUsage whose owningType is a ViewDefinition or ViewUsage must specialize the ViewUsage
+ // Views::View::subviews from the Systems Library Model.
+ var owningType = object.getOwningType();
+ if (owningType instanceof ViewDefinition || owningType instanceof ViewUsage) {
+ implicitSubsettingElement = "Views::View::subviews";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ @Override
+ public List caseWhileLoopActionUsage(WhileLoopActionUsage object) {
+ if (!this.implicitSpecializations.hasSubSetting()) {
+ var implicitSubsettingElement = "Actions::whileLoopActions";
+ // A composite WhileLoopActionUsage that is a subaction usage must directly or indirectly specialize the
+ // ActionUsage Actions::Action::whileLoops from the Systems Model Library.
+ if (object.isIsComposite() && object.isSubactionUsage()) {
+ implicitSubsettingElement = "Actions::Action::whileLoops";
+ }
+ var implicitSubsetting = this.implicitSubsetting(object, implicitSubsettingElement);
+ if (implicitSubsetting != null) {
+ this.implicitSpecializations.add(implicitSubsetting);
+ }
+ }
+ // Return null to iterate on other abstract EClass cases
+ return null;
+ }
+
+ private List getBaseTypes(MetadataUsage metadaUsage) {
+ String baseTypeFeatureQn = "Metaobjects::SemanticMetadata::baseType";
+ Feature baseTypeFeature = this.namespaceProvider.getNamespaceFromLibrary(baseTypeFeatureQn, Feature.class);
+ Expression valueExpression = metadaUsage.getMetadataDefinition().getOwnedFeature().stream()
+ .filter(f -> f.supertypes(true).contains(baseTypeFeature))
+ .findFirst()
+ .map(this::getValue)
+ .orElse(null);
+
+ if (valueExpression instanceof OperatorExpression opExpression && "meta".equals(opExpression.getOperator())) {
+ return opExpression.getParameter().stream()
+ .map(this::getValue)
+ .filter(MetadataAccessExpression.class::isInstance)
+ .map(MetadataAccessExpression.class::cast)
+ .map(MetadataAccessExpression::getReferencedElement)
+ .filter(Type.class::isInstance)
+ .map(Type.class::cast)
+ .toList();
+ } else {
+ return List.of();
+ }
+ }
+
+ private Expression getValue(Feature f) {
+ return f.getOwnedMembership().stream()
+ .filter(FeatureValue.class::isInstance)
+ .map(FeatureValue.class::cast)
+ .findFirst()
+ .map(FeatureValue::getValue)
+ .orElse(null);
+ }
+
+ private Redefinition implicitRedefinition(Feature feature, String implicitRedefinedFeatureQualifiedName) {
+ var implicitFeature = this.namespaceProvider.getNamespaceFromLibrary(implicitRedefinedFeatureQualifiedName, Feature.class);
+ if (implicitFeature != null) {
+ return this.implicitRedefinition(feature, implicitFeature);
+ }
+ return null;
+ }
+
+ private Redefinition implicitRedefinition(Feature redefiningFeature, Feature redefinedFeature) {
+ var redefinition = SysmlFactory.eINSTANCE.createRedefinition();
+ redefinition.setDeclaredName("redefines (implicit)");
+ redefinition.setIsImplied(true);
+ redefinition.setRedefiningFeature(redefiningFeature);
+ redefinition.setRedefinedFeature(redefinedFeature);
+ return redefinition;
+ }
+
+ private Subclassification implicitSubclassification(Classifier classifier, String implicitSuperclassifierQualifiedName) {
+ var implicitClassifier = this.namespaceProvider.getNamespaceFromLibrary(implicitSuperclassifierQualifiedName, Classifier.class);
+ if (implicitClassifier != null) {
+ return this.implicitSubclassification(classifier, implicitClassifier);
+ }
+ return null;
+ }
+
+ private Subclassification implicitSubclassification(Classifier classifier, Classifier implicitClassifier) {
+ var subclassification = SysmlFactory.eINSTANCE.createSubclassification();
+ subclassification.setDeclaredName("subclasses (implicit)");
+ subclassification.setIsImplied(true);
+ subclassification.setSubclassifier(classifier);
+ subclassification.setSuperclassifier(implicitClassifier);
+ return subclassification;
+ }
+
+ private Subsetting implicitSubsetting(Feature feature, String implicitSubsettedFeatureQualifiedName) {
+ var implicitFeature = this.namespaceProvider.getNamespaceFromLibrary(implicitSubsettedFeatureQualifiedName, Feature.class);
+ if (implicitFeature != null) {
+ return this.implicitSubsetting(feature, implicitFeature);
+ }
+ return null;
+ }
+
+ private Subsetting implicitSubsetting(Feature feature, Feature implicitFeature) {
+ var subsetting = SysmlFactory.eINSTANCE.createSubsetting();
+ subsetting.setDeclaredName("subsets (implicit)");
+ subsetting.setIsImplied(true);
+ subsetting.setSubsettingFeature(feature);
+ subsetting.setSubsettedFeature(implicitFeature);
+ return subsetting;
+ }
+
+ private Subsetting implicitReferenceSubsetting(Feature feature, Feature implicitFeature) {
+ var subsetting = SysmlFactory.eINSTANCE.createReferenceSubsetting();
+ subsetting.setDeclaredName("subsets (implicit)");
+ subsetting.setIsImplied(true);
+ subsetting.setSubsettingFeature(feature);
+ subsetting.setSubsettedFeature(implicitFeature);
+ return subsetting;
+ }
+
+ private FeatureTyping implicitTyping(Feature feature, String implicitTypeQualifiedName) {
+ var implicitType = this.namespaceProvider.getNamespaceFromLibrary(implicitTypeQualifiedName, Type.class);
+ if (implicitType != null) {
+ var featureTyping = SysmlFactory.eINSTANCE.createFeatureTyping();
+ featureTyping.setDeclaredName("typed by (implicit)");
+ featureTyping.setIsImplied(true);
+ featureTyping.setTypedFeature(feature);
+ featureTyping.setType(implicitType);
+ return featureTyping;
+ }
+ return null;
+ }
+
+ /**
+ * Handle special case of {@link ReferenceUsage} inside the {@link EndFeatureMembership} of a
+ * {@link SuccessionAsUsage}.
+ *
+ *
+ * The first two ReferenceUsage of a SuccessionAsUsage point to the source and target. If those ReferenceUsages do
+ * not explicitly define a {@link ReferenceSubsetting} then a {@link ReferenceSubsetting} is computed from the
+ * previous and next feature.
+ *
+ *
+ * @param referenceUsage
+ * the {@link ReferenceUsage} that might need modificationU
+ * @param parentSuccessionAsUsage
+ * the parent element of the given {@link ReferenceUsage}
+ * @return a list of {@link Specialization}
+ */
+ private List handleReferenceUsageInSuccessionAsUsage(ReferenceUsage referenceUsage, SuccessionAsUsage parentSuccessionAsUsage) {
+ final List result;
+ // At this moment we only handle the case of implicit source since we haven't found a
+ // case where the target is implicit
+ int index = parentSuccessionAsUsage.getOwnedFeature().indexOf(referenceUsage);
+ boolean hasNoExpliciteSubsetting = this.getOwnedRelations(ReferenceSubsetting.class, referenceUsage).isEmpty();
+ if (index == 0 && this.getOwnedRelations(ReferenceSubsetting.class, referenceUsage).isEmpty()) {
+ // Source feature
+ // Add a reference ReferenceSubsetting to the previous feature
+ Feature sourceFeature = this.computeSourceFeature(parentSuccessionAsUsage);
+ result = List.of(this.implicitReferenceSubsetting(referenceUsage, sourceFeature));
+ } else if (index == 1 && hasNoExpliciteSubsetting) {
+ // Target feature
+ // Add a reference ReferenceSubsetting to the next feature
+ Feature targetFeature = this.computeTargetFeature(parentSuccessionAsUsage);
+ result = List.of(this.implicitReferenceSubsetting(referenceUsage, targetFeature));
+ } else {
+ result = List.of();
+ }
+ return result;
+ }
+
+ private List getOwnedRelations(java.lang.Class type, Element parent) {
+ return parent.getOwnedRelationship().stream()
+ .filter(type::isInstance)
+ .map(type::cast)
+ .toList();
+ }
+
+ private Feature computeSourceFeature(SuccessionAsUsage successionAsUsage) {
+ Feature computeSourceFeature = null;
+ Namespace owningNamespace = successionAsUsage.getOwningNamespace();
+ if (owningNamespace instanceof TransitionUsage transitionUsage && transitionUsage.getSuccession() == successionAsUsage) {
+ computeSourceFeature = transitionUsage.getSource();
+ } else if (owningNamespace != null) {
+ EList ownedMemberships = owningNamespace.getOwnedMembership();
+ int index = ownedMemberships.indexOf(successionAsUsage.getOwningMembership());
+ if (index > 0) {
+ ListIterator iterator = ownedMemberships.subList(0, index).listIterator(index);
+ while (iterator.hasPrevious()) {
+ Membership previous = iterator.previous();
+ if (previous.getMemberElement() instanceof Feature feature && this.isValidSourceOrTargetFeaturForSuccession(feature)) {
+ return feature;
+ }
+ }
+ }
+ }
+ return computeSourceFeature;
+ }
+
+ private Feature computeTargetFeature(SuccessionAsUsage successionAsUsage) {
+ Feature computeTargetFeature = null;
+ Namespace owningNamespace = successionAsUsage.getOwningNamespace();
+ if (owningNamespace instanceof TransitionUsage transitionUsage && transitionUsage.getSuccession() == successionAsUsage) {
+ computeTargetFeature = transitionUsage.getTarget();
+ } else if (owningNamespace != null) {
+ EList ownedMemberships = owningNamespace.getOwnedMembership();
+ int index = ownedMemberships.indexOf(successionAsUsage.getOwningMembership());
+ if (index > 0 && ownedMemberships.size() > index) {
+ ListIterator iterator = ownedMemberships.subList(index + 1, ownedMemberships.size()).listIterator();
+ while (iterator.hasNext()) {
+ Membership next = iterator.next();
+ if (next.getMemberElement() instanceof Feature feature && this.isValidSourceOrTargetFeaturForSuccession(feature)) {
+ return feature;
+ }
+ }
+ }
+ }
+ return computeTargetFeature;
+ }
+
+ private boolean isValidSourceOrTargetFeaturForSuccession(Feature feature) {
+ return !(feature instanceof Connector || feature instanceof TransitionUsage);
+ }
+
+ /**
+ * Handle the creation of implicit redefinitions for feature that are parameters of their owner.
+ *
+ * This method implements KerML 7.4.7.2 and 7.4.7.3, and ensures that a parameter implicitly redefines the
+ * corresponding parameters of its owner's specializations that are {@link Behavior} or {@link Step}.
+ *
+ *
+ * @param feature
+ * the feature to redefine
+ * @return the list of {@link Redefinition} for the provided {@code feature}
+ */
+ private List handleImplicitParameterRedefinition(Feature feature) {
+ List implicitRedefinitions = new ArrayList<>();
+ if (feature.getOwner() instanceof Step stepOwner) {
+ implicitRedefinitions = this.handleImplicitParameterRedefinition(feature, stepOwner);
+ } else if (feature.getOwner() instanceof Behavior behaviorOwner) {
+ implicitRedefinitions = this.handleImplicitParameterRedefinition(feature, behaviorOwner);
+ }
+ return implicitRedefinitions;
+ }
+
+ private List handleImplicitParameterRedefinition(Feature feature, Step owner) {
+ List implicitRedefinitions = new ArrayList<>();
+ int parameterIndex = owner.getParameter().indexOf(feature);
+ if (parameterIndex >= 0) {
+ implicitRedefinitions = owner.getOwnedSpecialization().stream()
+ .map(Specialization::getGeneral)
+ .filter(type -> type instanceof Behavior || type instanceof Step)
+ .map(type -> this.createImplicitParameterRedefinition(feature, type, parameterIndex))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .toList();
+ }
+ return implicitRedefinitions;
+ }
+
+ private List handleImplicitParameterRedefinition(Feature feature, Behavior owner) {
+ List implicitRedefinitions = new ArrayList<>();
+ int parameterIndex = owner.getParameter().indexOf(feature);
+ if (parameterIndex >= 0) {
+ implicitRedefinitions = owner.getOwnedSubclassification().stream()
+ .map(Subclassification::getSuperclassifier)
+ .filter(Behavior.class::isInstance)
+ .map(classifier -> this.createImplicitParameterRedefinition(feature, classifier, parameterIndex))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .toList();
+ }
+ return implicitRedefinitions;
+ }
+
+ /**
+ * Creates an implicit redefinition from {@code feature} to the {@code index} parameter of {@code type}.
+ *
+ * This method creates the redefinition if:
+ *
+ * - The provided {@code type} contains parameters
+ * - The {@code index} parameter of {@code type} exists
+ * - The {@code index} parameter of {@code type} has the same direction as the provided {@code feature}.
+ *
+ *
+ *
+ * @param feature
+ * the feature redefining the parameter
+ * @param type
+ * the type containing the redefined parameter
+ * @param index
+ * the index of the redefined parameter in {@code type}
+ * @return
+ */
+ private Optional createImplicitParameterRedefinition(Feature feature, Type type, int index) {
+ Optional result = Optional.empty();
+ List parameters = new ArrayList<>();
+ if (type instanceof Behavior behavior) {
+ parameters = behavior.getParameter();
+ } else if (type instanceof Step step) {
+ parameters = step.getParameter();
+ }
+ if (parameters.size() > index) {
+ if (parameters.get(index).getDirection() == feature.getDirection()) {
+ result = Optional.ofNullable(this.implicitRedefinition(feature, parameters.get(index)));
+ }
+ }
+ return result;
+ }
+}
diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/LabelConstants.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/LabelConstants.java
new file mode 100644
index 000000000..44d2702ee
--- /dev/null
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/LabelConstants.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2023, 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.sysml.metamodel.helper;
+
+import org.eclipse.syson.sysml.metamodel.services.textual.SysMLv2Keywords;
+
+/**
+ * Label-related constants.
+ *
+ * @author arichard
+ */
+public class LabelConstants {
+
+ public static final String ABSTRACT = SysMLv2Keywords.ABSTRACT;
+
+ public static final String CLOSE_BRACKET = SysMLv2Keywords.RIGHT_BRACKET;
+
+ public static final String CLOSE_PARENTHESIS = SysMLv2Keywords.RIGHT_PAREN;
+
+ public static final String CLOSE_QUOTE = "\u00BB";
+
+ public static final String COLON = SysMLv2Keywords.COLON;
+
+ public static final String COLON_EQUAL = SysMLv2Keywords.ASSIGNMENT;
+
+ public static final String COMMA = SysMLv2Keywords.COMMA;
+
+ public static final String CONJUGATED = SysMLv2Keywords.TILDE;
+
+ public static final String CONSTANT = SysMLv2Keywords.CONSTANT;
+
+ public static final String CR = "\n";
+
+ public static final String DEFAULT = SysMLv2Keywords.DEFAULT;
+
+ public static final String DERIVED = SysMLv2Keywords.DERIVED;
+
+ public static final String END = SysMLv2Keywords.END;
+
+ 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;
+
+ public static final String INOUT = SysMLv2Keywords.INOUT;
+
+ public static final String LESSER_THAN = SysMLv2Keywords.LESS_THAN;
+
+ public static final String NON_UNIQUE = SysMLv2Keywords.NONUNIQUE;
+
+ public static final String OPEN_BRACKET = SysMLv2Keywords.LEFT_BRACKET;
+
+ public static final String OPEN_PARENTHESIS = SysMLv2Keywords.LEFT_PAREN;
+
+ public static final String OPEN_QUOTE = "\u00AB";
+
+ public static final String ORDERED = SysMLv2Keywords.ORDERED;
+
+ public static final String OUT = SysMLv2Keywords.OUT;
+
+ public static final String REDEFINITION = SysMLv2Keywords.REDEFINES_OPERATOR;
+
+ public static final String REF = SysMLv2Keywords.REF;
+
+ public static final String REFERENCES = SysMLv2Keywords.DOUBLE_COLON_SPECIALIZES;
+
+ public static final String SATISFY = SysMLv2Keywords.SATISFY;
+
+ public static final String SPACE = " ";
+
+ public static final String SUBCLASSIFICATION = SysMLv2Keywords.SPECIALIZES_OPERATOR;
+
+ public static final String SUBSETTING = SysMLv2Keywords.SPECIALIZES_OPERATOR;
+
+ public static final String TIMESLICE = SysMLv2Keywords.TIMESLICE;
+
+ public static final String SNAPSHOT = SysMLv2Keywords.SNAPSHOT;
+
+ public static final String VARIANT = SysMLv2Keywords.VARIANT;
+
+ public static final String VARIATION = SysMLv2Keywords.VARIATION;
+}
diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/MembershipComputer.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/MembershipComputer.java
new file mode 100644
index 000000000..29fdc15aa
--- /dev/null
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/MembershipComputer.java
@@ -0,0 +1,199 @@
+/*******************************************************************************
+ * Copyright (c) 2024, 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.sysml.metamodel.helper;
+
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.eclipse.emf.common.util.BasicEList;
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.InternalEObject;
+import org.eclipse.emf.ecore.util.EcoreEList;
+import org.eclipse.syson.sysml.Conjugation;
+import org.eclipse.syson.sysml.Element;
+import org.eclipse.syson.sysml.Import;
+import org.eclipse.syson.sysml.Membership;
+import org.eclipse.syson.sysml.MembershipImport;
+import org.eclipse.syson.sysml.Namespace;
+import org.eclipse.syson.sysml.NamespaceImport;
+import org.eclipse.syson.sysml.Specialization;
+import org.eclipse.syson.sysml.SysmlPackage;
+import org.eclipse.syson.sysml.Type;
+import org.eclipse.syson.sysml.VisibilityKind;
+
+/**
+ * Object in charge of computing.
+ *
+ * - visibleMemberships
+ * - inheritedMemberships
+ * - importedMemberships
+ *
+ *
+ * @param
+ * the type on which to apply the computation
+ * @author Arthur Daussy
+ */
+public class MembershipComputer {
+
+ private final Set visited;
+
+ private final T sourceElement;
+
+ public MembershipComputer(T sourceElement, EList extends Namespace> excluded) {
+ this.visited = excluded.stream().collect(toSet());
+ this.sourceElement = sourceElement;
+ }
+
+ public EList visibleMemberships(boolean isRecursive, boolean includeAll, boolean includeProtectedInherited) {
+ if (this.sourceElement instanceof Namespace namespace) {
+ return this.visibleMemberships(namespace, isRecursive, includeAll, includeProtectedInherited);
+ } else {
+ return new BasicEList<>();
+ }
+ }
+
+ private EList visibleMemberships(Namespace self, boolean isRecursive, boolean includeAll, boolean includeProtectedInherited) {
+ if (this.visited.contains(self)) {
+ return new BasicEList<>();
+ }
+
+ // Protected against infinite loop while iterating on imported/inherited elements
+ this.visited.add(self);
+
+ NameConflictingFilter nameConflictingFilter = new NameConflictingFilter();
+ List directMemberships = self.getOwnedMembership().stream()
+ .filter(m -> includeAll || m.getVisibility() == VisibilityKind.PUBLIC)
+ .filter(nameConflictingFilter)
+ .collect(toList());
+
+ self.getOwnedImport().stream()
+ .filter(m -> includeAll || m.getVisibility() == VisibilityKind.PUBLIC)
+ .flatMap(imp -> this.importedMemberships(imp).stream())
+ .filter(nameConflictingFilter)
+ .forEach(directMemberships::add);
+
+ if (self instanceof Type type) {
+
+ this.inheritedMemberships(type).stream()
+ .filter(rel -> includeAll || (includeProtectedInherited && rel.getVisibility() == VisibilityKind.PROTECTED) || rel.getVisibility() == VisibilityKind.PUBLIC)
+ .filter(nameConflictingFilter)
+ .forEach(directMemberships::add);
+
+ }
+
+ BasicEList visibleMemberships = new BasicEList<>(directMemberships);
+
+ if (isRecursive) {
+ List recursiveMembers = new BasicEList<>();
+ for (Membership m : visibleMemberships) {
+ if (m.getMemberElement() instanceof Namespace subNamespace) {
+ if (!visibleMemberships.contains(subNamespace)) {
+ recursiveMembers.addAll(this.visibleMemberships(subNamespace, isRecursive, includeAll, includeProtectedInherited));
+ }
+ }
+ }
+ visibleMemberships.addAll(recursiveMembers);
+ }
+
+ return visibleMemberships;
+ }
+
+ public EList inheritedMemberships() {
+ if (this.sourceElement instanceof Type type) {
+ return this.inheritedMemberships(type);
+ } else {
+ return new BasicEList<>();
+ }
+ }
+
+ private EList inheritedMemberships(Type self) {
+ this.visited.add(self);
+
+ NameConflictingFilter namefilter = new NameConflictingFilter();
+ namefilter.fillUsedNames(self.getOwnedMembership());
+ Conjugation conjugator = self.getOwnedConjugator();
+ List conjugatedMemberships = List.of();
+ if (conjugator != null) {
+ Type type = conjugator.getOriginalType();
+ if (type != null) {
+ conjugatedMemberships = this.visibleMemberships(type, false, true, true).stream().filter(namefilter).toList();
+ }
+ }
+
+ List generalMemberships = new BasicEList<>();
+ for (Specialization specialization : self.getOwnedSpecialization()) {
+ Type general = specialization.getGeneral();
+ if (general != null && !this.visited.contains(general)) {
+ this.visibleMemberships(general, false, true, true).stream()
+ .filter(namefilter)
+ .forEach(generalMemberships::add);
+ }
+ }
+
+ Membership[] data = Stream.concat(conjugatedMemberships.stream(), generalMemberships.stream())
+ // Also inherit protected memberships
+ .filter(rel -> rel.getVisibility() != VisibilityKind.PRIVATE)
+ .toArray(Membership[]::new);
+ return new EcoreEList.UnmodifiableEList<>((InternalEObject) self, SysmlPackage.eINSTANCE.getType_InheritedMembership(), data.length, data);
+ }
+
+ public EList importedMemberships() {
+ if (this.sourceElement instanceof Import imp) {
+ return this.importedMemberships(imp);
+ } else {
+ return new BasicEList<>();
+ }
+ }
+
+ private EList importedMemberships(Import self) {
+ EList importedMemberships = new BasicEList<>();
+ if (self instanceof NamespaceImport nmImport) {
+ importedMemberships = this.importedMemberships(nmImport);
+ } else if (self instanceof MembershipImport msImport) {
+ importedMemberships = this.importedMemberships(msImport);
+ }
+ return importedMemberships;
+ }
+
+ private EList importedMemberships(MembershipImport msImport) {
+ BasicEList importedMemberships = new BasicEList<>();
+ Membership membership = msImport.getImportedMembership();
+ if (membership != null) {
+ Element member = membership.getMemberElement();
+ if (member != null) {
+ if (!msImport.isIsRecursive() || !(member instanceof Namespace)) {
+ importedMemberships.add(membership);
+ } else if (member instanceof Namespace namespace) {
+ if (!this.visited.contains(namespace)) {
+ importedMemberships.add(membership);
+ importedMemberships.addAll(this.visibleMemberships(namespace, msImport.isIsRecursive(), msImport.isIsImportAll(), false));
+ }
+ }
+ }
+ }
+ return importedMemberships;
+ }
+
+ private EList importedMemberships(NamespaceImport self) {
+ Namespace aImportedNamespace = self.getImportedNamespace();
+ BasicEList result = new BasicEList<>();
+ if (aImportedNamespace != null && !this.visited.contains(aImportedNamespace)) {
+ result.addAll(this.visibleMemberships(aImportedNamespace, self.isIsRecursive(), self.isIsImportAll(), false));
+ }
+ return result;
+ }
+}
diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/NameConflictingFilter.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/NameConflictingFilter.java
new file mode 100644
index 000000000..88d4f23b4
--- /dev/null
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/NameConflictingFilter.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2024, 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.sysml.metamodel.helper;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import org.eclipse.syson.sysml.Element;
+import org.eclipse.syson.sysml.Membership;
+
+/**
+ * Filter in charge of checking if element has a conflicting name or short name with a set a previously checked
+ * elements.
+ *
+ *
+ * This filter only checks for elements that has at least an {@link Element#effectiveName()} or an
+ * {@link Element#effectiveShortName()}. If an element has neither, it matches the given element since it can not create
+ * any name conflict.
+ *
+ *
+ * @author Arthur Daussy
+ */
+public class NameConflictingFilter implements Predicate {
+
+ private final Set usedNames = new HashSet<>();
+
+ @Override
+ public boolean test(Membership member) {
+ boolean test = true;
+ if (member != null) {
+ if (member.getMemberName() != null
+ && member.getMemberElement() != null
+ && member.getMemberName() != member.getMemberElement().getName()) {
+ test = this.checkConflictingNames(member.getMemberName());
+ } else {
+ Element memberElement = member.getMemberElement();
+ if (memberElement != null) {
+ test = this.checkConflictingElement(memberElement);
+ }
+ }
+ }
+
+ return test;
+ }
+
+ public boolean checkConflictingElement(Element memberElement) {
+ return this.checkConflictingNames(memberElement.effectiveName()) && this.checkConflictingNames(memberElement.effectiveShortName());
+ }
+
+ public boolean checkConflictingNames(String name) {
+
+ boolean hasName = name != null;
+ if (hasName) {
+ boolean hasConflictingName = hasName && this.usedNames.contains(name);
+
+ boolean noConflict = !hasConflictingName;
+ if (noConflict) {
+ // Only add to forbidden names if the element is not conflicting on both the shortname and the
+ // name
+ this.usedNames.add(name);
+ }
+ return noConflict;
+ } // Else if the element has neither a name or a short name it can not create name conflict
+ return true;
+ }
+
+ public void fillUsedNames(List existingMembers) {
+ for (Membership member : existingMembers) {
+ Element memberElement = member.getMemberElement();
+ if (memberElement != null) {
+ String name = memberElement.effectiveName();
+ String shortName = memberElement.effectiveShortName();
+ if (name != null) {
+ this.usedNames.add(name);
+ }
+ if (shortName != null) {
+ this.usedNames.add(shortName);
+ }
+ }
+ }
+ }
+
+}
diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/NameHelper.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/NameHelper.java
new file mode 100644
index 000000000..7e933c21a
--- /dev/null
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/helper/NameHelper.java
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Copyright (c) 2024, 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.sysml.metamodel.helper;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.commons.text.StringEscapeUtils;
+import org.eclipse.syson.sysml.metamodel.services.textual.SysMLv2Keywords;
+
+/**
+ * Set of methods to help to manipulate/escape name/qualified name of sysml elements.
+ *
+ * @author Guillaume Escande
+ */
+public final class NameHelper {
+
+ private static final java.util.regex.Pattern VALID_CHARS = Pattern.compile("[^a-zA-Z0-9_]");
+
+ /**
+ * Parse a qualified name to extract list of sections.
+ *
+ * @param qualifiedName
+ * qualified name to split
+ * @return
+ */
+ public List parseQualifiedName(String qualifiedName) {
+ return Arrays.asList(qualifiedName.split("::"));
+ }
+
+ /**
+ * Escape a string.
+ *
+ * @param str
+ * string to escape
+ * @return
+ */
+ public String escapeString(String str) {
+ return StringEscapeUtils.escapeJava(str);
+ }
+
+ /**
+ * Unescape a string.
+ *
+ * @param str
+ * string to unescape
+ * @return
+ */
+ public String unescapeString(String str) {
+ if (str != null && str.length() > 1 && str.startsWith("'") && str.endsWith("'")) {
+ return str.substring(1, str.length() - 1);
+ }
+ return str;
+ }
+
+ /**
+ * Set a String printable for SysMLv2 name.
+ *
+ * @param initialName
+ * string to set printable
+ * @return a String printable for SysMLv2 name
+ */
+ public String toPrintableName(String initialName) {
+ return this.toPrintableName(initialName, false);
+ }
+
+ /**
+ * Set a String printable for SysMLv2 name.
+ *
+ * @param initialName
+ * string to set printable
+ * @param escapeSingleQuotes
+ * whether the single quotes should be escaped or not inside the printable value
+ * @return a String printable for SysMLv2 name
+ */
+ public String toPrintableName(String initialName, boolean escapeSingleQuotes) {
+ String name;
+ if (initialName == null || initialName.isEmpty()) {
+ name = "";
+ } else if (VALID_CHARS.matcher(initialName).find() || !this.isLetterOrUnderscore(initialName.charAt(0)) || SysMLv2Keywords.KEYWORDS.contains(initialName.trim())) {
+ if (escapeSingleQuotes) {
+ name = '\'' + initialName.replaceAll("'", "\\\\'") + '\'';
+ } else {
+ name = '\'' + initialName + '\'';
+ }
+ } else {
+ name = initialName;
+ }
+ return name;
+ }
+
+ private boolean isLetterOrUnderscore(char c) {
+ return c == '_' || this.isLowerCaseLetter(c) || this.isUpperCaseLetter(c);
+ }
+
+ private boolean isUpperCaseLetter(char c) {
+ return c >= 'a' && c <= 'z';
+ }
+
+ private boolean isLowerCaseLetter(char c) {
+ return c >= 'A' && c <= 'Z';
+ }
+
+}
diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/ElementInitializerSwitch.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/ElementInitializerSwitch.java
index eae84c54a..6fcc283c3 100644
--- a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/ElementInitializerSwitch.java
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/ElementInitializerSwitch.java
@@ -61,7 +61,7 @@
import org.eclipse.syson.sysml.ViewDefinition;
import org.eclipse.syson.sysml.ViewUsage;
import org.eclipse.syson.sysml.VisibilityKind;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.eclipse.syson.sysml.util.SysmlSwitch;
/**
diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/FeatureChainComputer.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/FeatureChainComputer.java
index ff5801cbe..2d6d46a2f 100644
--- a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/FeatureChainComputer.java
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/FeatureChainComputer.java
@@ -29,7 +29,7 @@
import org.eclipse.syson.sysml.Specialization;
import org.eclipse.syson.sysml.SysmlPackage;
import org.eclipse.syson.sysml.Type;
-import org.eclipse.syson.sysml.helper.EMFUtils;
+import org.eclipse.syson.sysml.metamodel.helper.EMFUtils;
/**
* Object in charge of computing a chain of feature to access a {@link Feature} from one {@link Element}.
diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/SysMLElementSerializer.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/SysMLElementSerializer.java
similarity index 94%
rename from backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/SysMLElementSerializer.java
rename to backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/SysMLElementSerializer.java
index 3100c7ce5..a625cd3c2 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/SysMLElementSerializer.java
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/SysMLElementSerializer.java
@@ -10,15 +10,10 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual;
+package org.eclipse.syson.sysml.metamodel.services.textual;
import static java.util.stream.Collectors.joining;
-import static org.eclipse.syson.sysml.textual.utils.SysMLRelationPredicates.IS_DEFINITION_BODY_ITEM_MEMBER;
-import static org.eclipse.syson.sysml.textual.utils.SysMLRelationPredicates.IS_IMPORT;
-import static org.eclipse.syson.sysml.textual.utils.SysMLRelationPredicates.IS_MEMBERSHIP;
-import static org.eclipse.syson.sysml.textual.utils.SysMLRelationPredicates.IS_METADATA_USAGE;
-import java.lang.Class;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -36,15 +31,134 @@
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.InternalEObject;
-import org.eclipse.syson.sysml.*;
+import org.eclipse.syson.sysml.AcceptActionUsage;
+import org.eclipse.syson.sysml.ActionDefinition;
+import org.eclipse.syson.sysml.ActionUsage;
+import org.eclipse.syson.sysml.ActorMembership;
+import org.eclipse.syson.sysml.AllocationDefinition;
+import org.eclipse.syson.sysml.AllocationUsage;
+import org.eclipse.syson.sysml.AnalysisCaseUsage;
+import org.eclipse.syson.sysml.AssertConstraintUsage;
+import org.eclipse.syson.sysml.AssignmentActionUsage;
+import org.eclipse.syson.sysml.AttributeDefinition;
+import org.eclipse.syson.sysml.AttributeUsage;
+import org.eclipse.syson.sysml.CalculationDefinition;
+import org.eclipse.syson.sysml.Classifier;
+import org.eclipse.syson.sysml.CollectExpression;
+import org.eclipse.syson.sysml.Comment;
+import org.eclipse.syson.sysml.ConjugatedPortDefinition;
+import org.eclipse.syson.sysml.ConjugatedPortTyping;
+import org.eclipse.syson.sysml.ConnectionUsage;
+import org.eclipse.syson.sysml.Connector;
+import org.eclipse.syson.sysml.ConstraintUsage;
+import org.eclipse.syson.sysml.ControlNode;
+import org.eclipse.syson.sysml.DecisionNode;
+import org.eclipse.syson.sysml.Definition;
+import org.eclipse.syson.sysml.Documentation;
+import org.eclipse.syson.sysml.Element;
+import org.eclipse.syson.sysml.EndFeatureMembership;
+import org.eclipse.syson.sysml.EnumerationDefinition;
+import org.eclipse.syson.sysml.EnumerationUsage;
+import org.eclipse.syson.sysml.EventOccurrenceUsage;
+import org.eclipse.syson.sysml.ExhibitStateUsage;
+import org.eclipse.syson.sysml.Expose;
+import org.eclipse.syson.sysml.Expression;
+import org.eclipse.syson.sysml.Feature;
+import org.eclipse.syson.sysml.FeatureChainExpression;
+import org.eclipse.syson.sysml.FeatureChaining;
+import org.eclipse.syson.sysml.FeatureDirectionKind;
+import org.eclipse.syson.sysml.FeatureMembership;
+import org.eclipse.syson.sysml.FeatureReferenceExpression;
+import org.eclipse.syson.sysml.FeatureTyping;
+import org.eclipse.syson.sysml.FeatureValue;
+import org.eclipse.syson.sysml.FlowEnd;
+import org.eclipse.syson.sysml.FlowUsage;
+import org.eclipse.syson.sysml.ForkNode;
+import org.eclipse.syson.sysml.Import;
+import org.eclipse.syson.sysml.IncludeUseCaseUsage;
+import org.eclipse.syson.sysml.InterfaceDefinition;
+import org.eclipse.syson.sysml.InvocationExpression;
+import org.eclipse.syson.sysml.ItemDefinition;
+import org.eclipse.syson.sysml.ItemUsage;
+import org.eclipse.syson.sysml.JoinNode;
+import org.eclipse.syson.sysml.LibraryPackage;
+import org.eclipse.syson.sysml.LiteralBoolean;
+import org.eclipse.syson.sysml.LiteralExpression;
+import org.eclipse.syson.sysml.LiteralInfinity;
+import org.eclipse.syson.sysml.LiteralInteger;
+import org.eclipse.syson.sysml.LiteralRational;
+import org.eclipse.syson.sysml.LiteralString;
+import org.eclipse.syson.sysml.Membership;
+import org.eclipse.syson.sysml.MembershipExpose;
+import org.eclipse.syson.sysml.MembershipImport;
+import org.eclipse.syson.sysml.MergeNode;
+import org.eclipse.syson.sysml.Metaclass;
+import org.eclipse.syson.sysml.MetadataAccessExpression;
+import org.eclipse.syson.sysml.MetadataDefinition;
+import org.eclipse.syson.sysml.MetadataUsage;
+import org.eclipse.syson.sysml.MultiplicityRange;
+import org.eclipse.syson.sysml.Namespace;
+import org.eclipse.syson.sysml.NamespaceExpose;
+import org.eclipse.syson.sysml.NamespaceImport;
+import org.eclipse.syson.sysml.NullExpression;
+import org.eclipse.syson.sysml.ObjectiveMembership;
+import org.eclipse.syson.sysml.OccurrenceDefinition;
+import org.eclipse.syson.sysml.OccurrenceUsage;
+import org.eclipse.syson.sysml.OperatorExpression;
+import org.eclipse.syson.sysml.OwningMembership;
import org.eclipse.syson.sysml.Package;
-import org.eclipse.syson.sysml.helper.EMFUtils;
-import org.eclipse.syson.sysml.helper.LabelConstants;
-import org.eclipse.syson.sysml.textual.utils.Appender;
-import org.eclipse.syson.sysml.textual.utils.INameDeresolver;
-import org.eclipse.syson.sysml.textual.utils.Status;
-import org.eclipse.syson.sysml.textual.utils.SysMLKeywordSwitch;
-import org.eclipse.syson.sysml.textual.utils.SysMLRelationPredicates;
+import org.eclipse.syson.sysml.ParameterMembership;
+import org.eclipse.syson.sysml.PartDefinition;
+import org.eclipse.syson.sysml.PartUsage;
+import org.eclipse.syson.sysml.PayloadFeature;
+import org.eclipse.syson.sysml.PerformActionUsage;
+import org.eclipse.syson.sysml.PortDefinition;
+import org.eclipse.syson.sysml.PortUsage;
+import org.eclipse.syson.sysml.PortionKind;
+import org.eclipse.syson.sysml.Redefinition;
+import org.eclipse.syson.sysml.ReferenceSubsetting;
+import org.eclipse.syson.sysml.ReferenceUsage;
+import org.eclipse.syson.sysml.Relationship;
+import org.eclipse.syson.sysml.RenderingUsage;
+import org.eclipse.syson.sysml.RequirementConstraintMembership;
+import org.eclipse.syson.sysml.RequirementDefinition;
+import org.eclipse.syson.sysml.RequirementUsage;
+import org.eclipse.syson.sysml.ReturnParameterMembership;
+import org.eclipse.syson.sysml.SatisfyRequirementUsage;
+import org.eclipse.syson.sysml.SelectExpression;
+import org.eclipse.syson.sysml.SendActionUsage;
+import org.eclipse.syson.sysml.Specialization;
+import org.eclipse.syson.sysml.StakeholderMembership;
+import org.eclipse.syson.sysml.StateDefinition;
+import org.eclipse.syson.sysml.StateSubactionMembership;
+import org.eclipse.syson.sysml.StateUsage;
+import org.eclipse.syson.sysml.Subclassification;
+import org.eclipse.syson.sysml.SubjectMembership;
+import org.eclipse.syson.sysml.Subsetting;
+import org.eclipse.syson.sysml.Succession;
+import org.eclipse.syson.sysml.SuccessionAsUsage;
+import org.eclipse.syson.sysml.SysmlPackage;
+import org.eclipse.syson.sysml.TextualRepresentation;
+import org.eclipse.syson.sysml.TransitionFeatureKind;
+import org.eclipse.syson.sysml.TransitionFeatureMembership;
+import org.eclipse.syson.sysml.TransitionUsage;
+import org.eclipse.syson.sysml.TriggerInvocationExpression;
+import org.eclipse.syson.sysml.TriggerKind;
+import org.eclipse.syson.sysml.Type;
+import org.eclipse.syson.sysml.Usage;
+import org.eclipse.syson.sysml.UseCaseDefinition;
+import org.eclipse.syson.sysml.UseCaseUsage;
+import org.eclipse.syson.sysml.VerificationCaseUsage;
+import org.eclipse.syson.sysml.ViewUsage;
+import org.eclipse.syson.sysml.ViewpointDefinition;
+import org.eclipse.syson.sysml.VisibilityKind;
+import org.eclipse.syson.sysml.metamodel.helper.EMFUtils;
+import org.eclipse.syson.sysml.metamodel.helper.LabelConstants;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.Appender;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.INameDeresolver;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.Status;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.SysMLKeywordSwitch;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.SysMLRelationPredicates;
import org.eclipse.syson.sysml.util.SysmlSwitch;
/**
@@ -64,6 +178,8 @@ public class SysMLElementSerializer extends SysmlSwitch {
private final SysMLKeywordSwitch keywordProvider = new SysMLKeywordSwitch();
+ private final SysMLRelationPredicates relationPredicates = new SysMLRelationPredicates();
+
private final Consumer reportConsumer;
private final boolean needEscapeCharacter;
@@ -294,7 +410,10 @@ public String caseConnectionUsage(ConnectionUsage connectionUsage) {
List ends = this.appendConnectorPart(connectorPartBuilder, connectionUsage);
- List contentMemberships = connectionUsage.getOwnedMembership().stream().filter(IS_DEFINITION_BODY_ITEM_MEMBER).filter(e -> !ends.contains(e)).toList();
+ List contentMemberships = connectionUsage.getOwnedMembership().stream()
+ .filter(this.relationPredicates.isDefinitionBodyItemMember())
+ .filter(e -> !ends.contains(e))
+ .toList();
if (!declarationAndValueBuilder.isEmpty() || !contentMemberships.isEmpty()) {
// If the ConnectionUsage as a declaration, a value or some content we need to use the keyword connection
@@ -570,7 +689,9 @@ public String caseLibraryPackage(LibraryPackage libraryPackage) {
}
builder.append("library package ");
this.appendNameWithShortName(builder, libraryPackage);
- List children = libraryPackage.getOwnedRelationship().stream().filter(IS_MEMBERSHIP.and(IS_METADATA_USAGE.negate()).or(IS_IMPORT)).toList();
+ List children = libraryPackage.getOwnedRelationship().stream()
+ .filter(this.relationPredicates.isMembership().and(this.relationPredicates.isMetadataUsage().negate()).or(this.relationPredicates.isImport()))
+ .toList();
this.appendChildrenContent(builder, libraryPackage, children);
return builder.toString();
}
@@ -811,7 +932,9 @@ public String casePackage(Package pack) {
Appender builder = this.newAppender();
builder.append("package ");
this.appendNameWithShortName(builder, pack);
- List children = pack.getOwnedRelationship().stream().filter(IS_MEMBERSHIP.and(IS_METADATA_USAGE.negate()).or(IS_IMPORT)).toList();
+ List children = pack.getOwnedRelationship().stream()
+ .filter(this.relationPredicates.isMembership().and(this.relationPredicates.isMetadataUsage().negate()).or(this.relationPredicates.isImport()))
+ .toList();
this.appendChildrenContent(builder, pack, children);
return builder.toString();
}
@@ -1202,7 +1325,7 @@ public String caseSuccessionAsUsage(SuccessionAsUsage successionAsUsage) {
}
List children = successionAsUsage.getOwnedRelationship().stream()
- .filter(IS_DEFINITION_BODY_ITEM_MEMBER)
+ .filter(this.relationPredicates.isDefinitionBodyItemMember())
.toList();
this.appendChildrenContent(builder, successionAsUsage, children);
@@ -1231,15 +1354,7 @@ public String caseTextualRepresentation(TextualRepresentation textualRepresentat
@Override
public String caseTransitionFeatureMembership(TransitionFeatureMembership transitionFeatureMembership) {
- var builder = this.newAppender();
- // Weird code....
- if (transitionFeatureMembership.getKind() == TransitionFeatureKind.GUARD) {
- List expressions = transitionFeatureMembership.getOwnedRelatedElement().stream()
- .filter(Expression.class::isInstance)
- .map(Expression.class::cast)
- .map(this::doSwitch)
- .toList();
- } else {
+ if (transitionFeatureMembership.getKind() != TransitionFeatureKind.GUARD) {
this.reportConsumer.accept(Status.warning("TransitionFeatureMembership of kind {0} are not yet handled", transitionFeatureMembership.getKind()));
}
return super.caseTransitionFeatureMembership(transitionFeatureMembership);
@@ -1299,7 +1414,8 @@ public String caseTransitionUsage(TransitionUsage transitionUsage) {
}
// Append usage body (removed already handled element : Succession and guard
- List children = transitionUsage.getOwnedRelationship().stream().filter(IS_DEFINITION_BODY_ITEM_MEMBER)
+ List children = transitionUsage.getOwnedRelationship().stream()
+ .filter(this.relationPredicates.isDefinitionBodyItemMember())
.filter(e -> !(e instanceof ParameterMembership))
.toList();
this.appendChildrenContent(builder, transitionUsage, children);
@@ -1594,7 +1710,9 @@ private String serializeDeclarationWithModifiers(Appender builder, Usage usage,
}
private void appendDefinitionBody(Appender builder, Usage usage) {
- List children = usage.getOwnedRelationship().stream().filter(IS_DEFINITION_BODY_ITEM_MEMBER).toList();
+ List children = usage.getOwnedRelationship().stream()
+ .filter(this.relationPredicates.isDefinitionBodyItemMember())
+ .toList();
this.appendChildrenContent(builder, usage, children);
}
@@ -2394,7 +2512,7 @@ private void appendAnnotatedElements(Appender builder, Comment comment, EListnull if no direct container of the expected type
*/
- private T getDirectContainer(EObject element, Class expected) {
+ private T getDirectContainer(EObject element, java.lang.Class expected) {
EObject eContainer = element.eContainer();
T result = null;
if (expected.isInstance(eContainer)) {
@@ -2439,7 +2557,7 @@ private void appendControlNodePrefix(Appender builder, ControlNode controlNode)
private void appendActionNodeBody(Appender appender, ControlNode controlNode) {
this.appendChildrenContent(appender, controlNode, controlNode.getOwnedRelationship().stream()
- .filter(SysMLRelationPredicates.IS_ANNOTATING_ELEMENT)
+ .filter(this.relationPredicates.isAnnotatingElement())
.toList());
}
@@ -2483,7 +2601,7 @@ private void appendDecisionTransition(TransitionUsage transitionUsage, Appender
// Append usage body (removed already handled element : Succession and guard
List children = transitionUsage.getOwnedRelationship().stream()
- .filter(IS_DEFINITION_BODY_ITEM_MEMBER)
+ .filter(this.relationPredicates.isDefinitionBodyItemMember())
.filter(e -> !alreadyHandledElements.contains(e) && !(e instanceof ParameterMembership))
.toList();
this.appendChildrenContent(builder, transitionUsage, children);
@@ -2500,7 +2618,9 @@ private String getDefinitionKeyword(Definition def) {
private void appendDefinition(Appender builder, Definition definition) {
this.appendDefinitionDeclaration(builder, definition);
- List children = definition.getOwnedRelationship().stream().filter(IS_DEFINITION_BODY_ITEM_MEMBER).toList();
+ List children = definition.getOwnedRelationship().stream()
+ .filter(this.relationPredicates.isDefinitionBodyItemMember())
+ .toList();
this.appendChildrenContent(builder, definition, children);
}
diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/SysMLSerializingOptions.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/SysMLSerializingOptions.java
similarity index 95%
rename from backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/SysMLSerializingOptions.java
rename to backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/SysMLSerializingOptions.java
index 28f8fc40f..cb83429c9 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/SysMLSerializingOptions.java
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/SysMLSerializingOptions.java
@@ -10,11 +10,11 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual;
+package org.eclipse.syson.sysml.metamodel.services.textual;
import java.util.Objects;
-import org.eclipse.syson.sysml.textual.utils.INameDeresolver;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.INameDeresolver;
/**
* Option used in the SysmlSerializer.
diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/SysMLv2Keywords.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/SysMLv2Keywords.java
new file mode 100644
index 000000000..8d740ba34
--- /dev/null
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/SysMLv2Keywords.java
@@ -0,0 +1,259 @@
+/*******************************************************************************
+ * 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.sysml.metamodel.services.textual;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Gather all keywords reserved by the SysML V2 textual language.
+ *
+ * @author Arthur Daussy
+ */
+public final class SysMLv2Keywords {
+
+ // Symbols / Operators / Punctuation
+ public static final String LESS_THAN = "<";
+ public static final String GREATER_THAN = ">";
+ public static final String SEMICOLON = ";";
+
+ public static final String LEFT_BRACE = "{";
+ public static final String RIGHT_BRACE = "}";
+
+ public static final String COMMA = ",";
+ public static final String DOT = ".";
+
+ public static final String LEFT_PAREN = "(";
+ public static final String RIGHT_PAREN = ")";
+
+ public static final String LEFT_BRACKET = "[";
+ public static final String RIGHT_BRACKET = "]";
+
+ public static final String AT_SIGN = "@";
+ public static final String HASH = "#";
+ public static final String TILDE = "\u007E";
+
+ public static final String COLON = ":";
+ public static final String DOUBLE_COLON = "::";
+
+ public static final String STAR = "*";
+ public static final String DOUBLE_STAR = "**";
+
+ public static final String RANGE = "..";
+ public static final String OPTIONAL_NAVIGATION = ".?";
+
+ public static final String ARROW = "->";
+ public static final String IMPLIES = "=>";
+
+ public static final String EQUALS = "=";
+ public static final String ASSIGNMENT = ":=";
+
+ // SysML/DSL-specific operators (based on your tokens)
+ public static final String SPECIALIZES_OPERATOR = ":>";
+ public static final String REDEFINES_OPERATOR = ":>>";
+ public static final String DOUBLE_COLON_SPECIALIZES = "::>";
+
+ // Word tokens / Keywords
+ public static final String DEPENDENCY = "dependency";
+ public static final String FROM = "from";
+ public static final String TO = "to";
+ public static final String COMMENT = "comment";
+ public static final String ABOUT = "about";
+ public static final String LOCALE = "locale";
+ public static final String DOC = "doc";
+ public static final String REP = "rep";
+ public static final String LANGUAGE = "language";
+ public static final String METADATA = "metadata";
+ public static final String DEF = "def";
+ public static final String ABSTRACT = "abstract";
+ public static final String REF = "ref";
+ public static final String REDEFINES = "redefines";
+ public static final String STANDARD = "standard";
+ public static final String LIBRARY = "library";
+ public static final String PACKAGE = "package";
+ public static final String FILTER = "filter";
+ public static final String ALIAS = "alias";
+ public static final String FOR = "for";
+ public static final String IMPORT = "import";
+ public static final String ALL = "all";
+
+ public static final String PUBLIC = "public";
+ public static final String PRIVATE = "private";
+ public static final String PROTECTED = "protected";
+
+ public static final String SPECIALIZES = "specializes";
+ public static final String ORDERED = "ordered";
+ public static final String NONUNIQUE = "nonunique";
+ public static final String DEFINED = "defined";
+ public static final String BY = "by";
+ public static final String SUBSETS = "subsets";
+ public static final String REFERENCES = "references";
+ public static final String CROSSES = "crosses";
+ public static final String VARIATION = "variation";
+ public static final String VARIANT = "variant";
+
+ public static final String IN = "in";
+ public static final String OUT = "out";
+ public static final String INOUT = "inout";
+
+ public static final String DERIVED = "derived";
+ public static final String CONSTANT = "constant";
+ public static final String END = "end";
+ public static final String DEFAULT = "default";
+
+ public static final String ATTRIBUTE = "attribute";
+ public static final String ENUM = "enum";
+
+ public static final String OCCURRENCE = "occurrence";
+ public static final String INDIVIDUAL = "individual";
+ public static final String SNAPSHOT = "snapshot";
+ public static final String TIMESLICE = "timeslice";
+ public static final String EVENT = "event";
+ public static final String THEN = "then";
+ public static final String ITEM = "item";
+ public static final String PART = "part";
+ public static final String PORT = "port";
+
+ public static final String BINDING = "binding";
+ public static final String BIND = "bind";
+ public static final String SUCCESSION = "succession";
+ public static final String FIRST = "first";
+
+ public static final String CONNECTION = "connection";
+ public static final String CONNECT = "connect";
+ public static final String INTERFACE = "interface";
+
+ public static final String ALLOCATION = "allocation";
+ public static final String ALLOCATE = "allocate";
+ public static final String FLOW = "flow";
+ public static final String MESSAGE = "message";
+ public static final String OF = "of";
+
+ public static final String ACTION = "action";
+ public static final String PERFORM = "perform";
+ public static final String ACCEPT = "accept";
+ public static final String VIA = "via";
+ public static final String AT = "at";
+ public static final String AFTER = "after";
+ public static final String WHEN = "when";
+ public static final String SEND = "send";
+ public static final String ASSIGN = "assign";
+
+ public static final String IF = "if";
+ public static final String ELSE = "else";
+ public static final String WHILE = "while";
+ public static final String LOOP = "loop";
+ public static final String UNTIL = "until";
+ public static final String TERMINATE = "terminate";
+ public static final String MERGE = "merge";
+ public static final String DECIDE = "decide";
+ public static final String JOIN = "join";
+ public static final String FORK = "fork";
+
+ public static final String STATE = "state";
+ public static final String PARALLEL = "parallel";
+ public static final String ENTRY = "entry";
+ public static final String DO = "do";
+ public static final String EXIT = "exit";
+ public static final String EXHIBIT = "exhibit";
+ public static final String TRANSITION = "transition";
+ public static final String CALC = "calc";
+ public static final String RETURN = "return";
+
+ public static final String CONSTRAINT = "constraint";
+ public static final String ASSERT = "assert";
+ public static final String NOT = "not";
+
+ public static final String REQUIREMENT = "requirement";
+ public static final String SUBJECT = "subject";
+ public static final String ASSUME = "assume";
+ public static final String REQUIRE = "require";
+ public static final String FRAME = "frame";
+
+ public static final String ACTOR = "actor";
+ public static final String STAKEHOLDER = "stakeholder";
+ public static final String SATISFY = "satisfy";
+ public static final String CONCERN = "concern";
+ public static final String CASE = "case";
+ public static final String OBJECTIVE = "objective";
+ public static final String ANALYSIS = "analysis";
+ public static final String VERIFICATION = "verification";
+ public static final String VERIFY = "verify";
+ public static final String USE = "use";
+ public static final String INCLUDE = "include";
+ public static final String VIEW = "view";
+ public static final String RENDER = "render";
+ public static final String RENDERING = "rendering";
+ public static final String EXPOSE = "expose";
+ public static final String VIEWPOINT = "viewpoint";
+
+ /** All word-based DSL keywords. */
+ public static final Set KEYWORDS;
+
+ /** All operators / symbols / punctuation. */
+ public static final Set OPERATORS;
+
+ /** Union of KEYWORDS and OPERATORS. */
+ public static final Set ALL_TOKENS;
+
+ static {
+ KEYWORDS = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(
+ DEPENDENCY, FROM, TO, COMMENT, ABOUT, LOCALE, DOC, REP, LANGUAGE, METADATA, DEF, ABSTRACT, REF,
+ REDEFINES, STANDARD, LIBRARY, PACKAGE, FILTER, ALIAS, FOR, IMPORT, ALL,
+ PUBLIC, PRIVATE, PROTECTED,
+ SPECIALIZES, ORDERED, NONUNIQUE, DEFINED, BY, SUBSETS, REFERENCES, CROSSES,
+ VARIATION, VARIANT,
+ IN, OUT, INOUT,
+ DERIVED, CONSTANT, END, DEFAULT,
+ ATTRIBUTE, ENUM,
+ OCCURRENCE, INDIVIDUAL, SNAPSHOT, TIMESLICE, EVENT, THEN, ITEM, PART, PORT,
+ BINDING, BIND, SUCCESSION, FIRST,
+ CONNECTION, CONNECT, INTERFACE,
+ ALLOCATION, ALLOCATE, FLOW, MESSAGE, OF,
+ ACTION, PERFORM, ACCEPT, VIA, AT, AFTER, WHEN, SEND, ASSIGN,
+ IF, ELSE, WHILE, LOOP, UNTIL, TERMINATE, MERGE, DECIDE, JOIN, FORK,
+ STATE, PARALLEL, ENTRY, DO, EXIT, EXHIBIT, TRANSITION, CALC, RETURN,
+ CONSTRAINT, ASSERT, NOT,
+ REQUIREMENT, SUBJECT, ASSUME, REQUIRE, FRAME,
+ ACTOR, STAKEHOLDER, SATISFY, CONCERN, CASE, OBJECTIVE, ANALYSIS,
+ VERIFICATION, VERIFY,
+ USE, INCLUDE, VIEW, RENDER, RENDERING, EXPOSE, VIEWPOINT
+ )));
+
+ OPERATORS = Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(
+ LESS_THAN, GREATER_THAN, SEMICOLON,
+ LEFT_BRACE, RIGHT_BRACE,
+ COMMA, DOT,
+ LEFT_PAREN, RIGHT_PAREN,
+ LEFT_BRACKET, RIGHT_BRACKET,
+ AT_SIGN, HASH, TILDE,
+ COLON, DOUBLE_COLON,
+ STAR, DOUBLE_STAR,
+ RANGE, OPTIONAL_NAVIGATION,
+ ARROW, IMPLIES,
+ EQUALS, ASSIGNMENT,
+ SPECIALIZES_OPERATOR, REDEFINES_OPERATOR, DOUBLE_COLON_SPECIALIZES
+ )));
+
+ LinkedHashSet all = new LinkedHashSet<>(KEYWORDS.size() + OPERATORS.size());
+ all.addAll(KEYWORDS);
+ all.addAll(OPERATORS);
+ ALL_TOKENS = Collections.unmodifiableSet(all);
+ }
+
+ private SysMLv2Keywords() {
+ // Utility class
+ }
+}
diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/Appender.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/Appender.java
similarity index 92%
rename from backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/Appender.java
rename to backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/Appender.java
index 46c51e275..772cd8424 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/Appender.java
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/Appender.java
@@ -10,12 +10,12 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.utils;
+package org.eclipse.syson.sysml.metamodel.services.textual.utils;
import java.util.Collection;
import java.util.List;
-import org.eclipse.syson.sysml.helper.NameHelper;
+import org.eclipse.syson.sysml.metamodel.helper.NameHelper;
/**
* Object that concatenate string using some custom convention.
@@ -33,7 +33,6 @@ public class Appender {
private final Collection symbols = List.of('[', '(', '.', '@');
public Appender(String newLine, String indentation) {
- super();
this.newLine = newLine;
this.indentation = indentation;
}
@@ -42,12 +41,8 @@ public boolean isEmpty() {
return this.builder.isEmpty() || this.builder.toString().trim().isBlank();
}
- public static String toPrintableName(String initialName) {
- return NameHelper.toPrintableName(initialName, true);
- }
-
public Appender appendPrintableName(String name) {
- this.append(toPrintableName(name));
+ this.append(new NameHelper().toPrintableName(name, true));
return this;
}
diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/FileNameDeresolver.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/FileNameDeresolver.java
similarity index 94%
rename from backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/FileNameDeresolver.java
rename to backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/FileNameDeresolver.java
index b02b4c88a..092b02147 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/FileNameDeresolver.java
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/FileNameDeresolver.java
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.utils;
+package org.eclipse.syson.sysml.metamodel.services.textual.utils;
import static java.util.stream.Collectors.toCollection;
@@ -35,10 +35,10 @@
import org.eclipse.syson.sysml.Namespace;
import org.eclipse.syson.sysml.SysmlPackage;
import org.eclipse.syson.sysml.VisibilityKind;
-import org.eclipse.syson.sysml.helper.DeresolvingNamespaceProvider;
-import org.eclipse.syson.sysml.helper.EMFUtils;
-import org.eclipse.syson.sysml.helper.MembershipComputer;
-import org.eclipse.syson.sysml.helper.NameHelper;
+import org.eclipse.syson.sysml.metamodel.helper.DeresolvingNamespaceProvider;
+import org.eclipse.syson.sysml.metamodel.helper.EMFUtils;
+import org.eclipse.syson.sysml.metamodel.helper.MembershipComputer;
+import org.eclipse.syson.sysml.metamodel.helper.NameHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -90,7 +90,7 @@ public String getDeresolvedName(Element element, Element context) {
for (Namespace deresolvingNamespace : deresolvingNamespaces) {
// Check if resolving the direct identifier works (declaredShortName or declaredName)
- String directIdentifier = getResolvableDirectIdentifier(element, deresolvingNamespace);
+ String directIdentifier = this.getResolvableDirectIdentifier(element, deresolvingNamespace);
if (directIdentifier != null) {
qualifiedNames.add(directIdentifier);
} else {
@@ -245,7 +245,7 @@ private String buildRelativeQualifiedName(Element element, Namespace owningNames
* the namespace used to test the resolution of the identifier
* @return an identifier or null if no direct resolvable identifier found.
*/
- private static String getResolvableDirectIdentifier(Element targetElement, Namespace contextNamespace) {
+ private String getResolvableDirectIdentifier(Element targetElement, Namespace contextNamespace) {
String identifier = null;
String shortName = targetElement.getDeclaredShortName();
if (shortName != null && !shortName.isBlank()) {
@@ -266,7 +266,7 @@ private static String getResolvableDirectIdentifier(Element targetElement, Names
}
if (identifier != null) {
- identifier = NameHelper.toPrintableName(identifier);
+ identifier = new NameHelper().toPrintableName(identifier);
}
return identifier;
}
@@ -301,9 +301,9 @@ private String getRelativeQualifiedName(String elementQn, Element element, Membe
String importedElementQualifiedName = this.getQualifiedName(importedElement);
if (importedElement != element && !importedElementQualifiedName.isEmpty()) {
int partToRemove = importedElementQualifiedName.length() + 2;
- qn = Appender.toPrintableName(importedElement.getName()) + "::" + elementQn.substring(partToRemove);
+ qn = new NameHelper().toPrintableName(importedElement.getName()) + "::" + elementQn.substring(partToRemove);
} else {
- qn = Appender.toPrintableName(element.getName());
+ qn = new NameHelper().toPrintableName(element.getName());
}
return qn;
}
diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/INameDeresolver.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/INameDeresolver.java
similarity index 77%
rename from backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/INameDeresolver.java
rename to backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/INameDeresolver.java
index 686b3bf00..2576b2b30 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/INameDeresolver.java
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/INameDeresolver.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2025 Obeo.
+ * Copyright (c) 2025, 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
@@ -10,10 +10,15 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.utils;
+package org.eclipse.syson.sysml.metamodel.services.textual.utils;
import org.eclipse.syson.sysml.Element;
+/**
+ * Retrieve the name of the given element from a given context.
+ *
+ * @author arichard
+ */
public interface INameDeresolver {
/**
@@ -25,6 +30,6 @@ public interface INameDeresolver {
* the context element from which the deresolution occurs
* @return a name or null
*/
- public String getDeresolvedName(Element element, Element context);
+ String getDeresolvedName(Element element, Element context);
}
diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/Severity.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/Severity.java
similarity index 85%
rename from backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/Severity.java
rename to backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/Severity.java
index e238c1e43..1bd09a9e0 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/Severity.java
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/Severity.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.utils;
+package org.eclipse.syson.sysml.metamodel.services.textual.utils;
/**
* Severity.
diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/Status.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/Status.java
similarity index 96%
rename from backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/Status.java
rename to backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/Status.java
index f08b36cb2..98e34967d 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/Status.java
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/Status.java
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.utils;
+package org.eclipse.syson.sysml.metamodel.services.textual.utils;
import java.text.MessageFormat;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/SysMLKeywordSwitch.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/SysMLKeywordSwitch.java
similarity index 97%
rename from backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/SysMLKeywordSwitch.java
rename to backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/SysMLKeywordSwitch.java
index 41376da5a..6f05bad32 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/utils/SysMLKeywordSwitch.java
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/SysMLKeywordSwitch.java
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.utils;
+package org.eclipse.syson.sysml.metamodel.services.textual.utils;
import org.eclipse.emf.ecore.EObject;
@@ -42,7 +42,7 @@
import org.eclipse.syson.sysml.RequirementDefinition;
import org.eclipse.syson.sysml.RequirementUsage;
import org.eclipse.syson.sysml.SubjectMembership;
-import org.eclipse.syson.sysml.textual.SysMLv2Keywords;
+import org.eclipse.syson.sysml.metamodel.services.textual.SysMLv2Keywords;
import org.eclipse.syson.sysml.util.SysmlSwitch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/SysMLRelationPredicates.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/SysMLRelationPredicates.java
new file mode 100644
index 000000000..e0191d175
--- /dev/null
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/utils/SysMLRelationPredicates.java
@@ -0,0 +1,147 @@
+/*******************************************************************************
+ * Copyright (c) 2024, 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.sysml.metamodel.services.textual.utils;
+
+import java.util.function.Predicate;
+import java.util.stream.Stream;
+
+import org.eclipse.syson.sysml.AnnotatingElement;
+import org.eclipse.syson.sysml.AttributeUsage;
+import org.eclipse.syson.sysml.BindingConnectorAsUsage;
+import org.eclipse.syson.sysml.Definition;
+import org.eclipse.syson.sysml.Dependency;
+import org.eclipse.syson.sysml.Element;
+import org.eclipse.syson.sysml.EnumerationUsage;
+import org.eclipse.syson.sysml.Import;
+import org.eclipse.syson.sysml.Membership;
+import org.eclipse.syson.sysml.MetadataFeature;
+import org.eclipse.syson.sysml.MetadataUsage;
+import org.eclipse.syson.sysml.OccurrenceUsage;
+import org.eclipse.syson.sysml.OwningMembership;
+import org.eclipse.syson.sysml.ReferenceUsage;
+import org.eclipse.syson.sysml.Relationship;
+import org.eclipse.syson.sysml.SuccessionAsUsage;
+import org.eclipse.syson.sysml.SysmlPackage;
+import org.eclipse.syson.sysml.VariantMembership;
+
+/**
+ * List of predicates to select Relationships.
+ *
+ * @author Arthur Daussy
+ */
+public class SysMLRelationPredicates {
+
+ private final Predicate isNotUserDefineKeywordMember = r -> !this.isMemberElementA(r, MetadataUsage.class);
+
+ private final Predicate isMembership = r -> r instanceof Membership;
+
+ private final Predicate isImport = r -> r instanceof Import;
+
+ private final Predicate isMetadataUsage = m -> this.isMemberElementA(m, MetadataUsage.class);
+
+ private final Predicate isNonOccurrenceUsageMember = r -> this.isMemberElementA(r, Definition.class, ReferenceUsage.class,
+ AttributeUsage.class, EnumerationUsage.class, BindingConnectorAsUsage.class, SuccessionAsUsage.class) || this.isExtendedUsage(r);
+
+
+ // https://issues.omg.org/issues/KERML-307
+ private final Predicate isAnnotatingElement = r -> this.isMemberElementA(r, AnnotatingElement.class)
+ && !this.isMemberElementA(r, MetadataFeature.class);
+
+ private final Predicate isDefinitionMember = r -> this.isMemberElementA(r, Definition.class, Package.class)
+ || this.isAnnotatingElement.test(r) || this.isA(r, Dependency.class);
+
+ private final Predicate isVariantUsageMember = r -> this.isA(r, VariantMembership.class);
+
+ private final Predicate isStructureUsageElementMember = r -> this.isMemberElementA(r, OccurrenceUsage.class);
+
+ private final Predicate isOccurrenceUsageMember = r -> this.isMemberElementA(r, OccurrenceUsage.class);
+
+ private final Predicate isAliasMember = r -> r instanceof Membership membership && !(r instanceof OwningMembership)
+ && (membership.getMemberName() != null || membership.getShortName() != null);
+
+ private final Predicate isDefinitionBodyItemMember = this.isNotUserDefineKeywordMember.and(
+ this.isDefinitionMember
+ .or(this.isVariantUsageMember)
+ .or(this.isNonOccurrenceUsageMember)
+ .or(this.isOccurrenceUsageMember)
+ .or(this.isAliasMember).or(this.isImport));
+
+ public Predicate isNotUserDefineKeywordMember() {
+ return this.isNotUserDefineKeywordMember;
+ }
+
+ public Predicate isMembership() {
+ return this.isMembership;
+ }
+
+ public Predicate isImport() {
+ return this.isImport;
+ }
+
+ public Predicate isMetadataUsage() {
+ return this.isMetadataUsage;
+ }
+
+ public Predicate isNonOccurrenceUsageMember() {
+ return this.isNonOccurrenceUsageMember;
+ }
+
+ public Predicate isAnnotatingElement() {
+ return this.isAnnotatingElement;
+ }
+
+ public Predicate isDefinitionMember() {
+ return this.isDefinitionMember;
+ }
+
+ public Predicate isVariantUsageMember() {
+ return this.isVariantUsageMember;
+ }
+
+ public Predicate isStructureUsageElementMember() {
+ return this.isStructureUsageElementMember;
+ }
+
+ public Predicate isOccurrenceUsageMember() {
+ return this.isOccurrenceUsageMember;
+ }
+
+ public Predicate isAliasMember() {
+ return this.isAliasMember;
+ }
+
+ public Predicate isDefinitionBodyItemMember() {
+ return this.isDefinitionBodyItemMember;
+ }
+
+ private boolean isMemberElementA(Relationship r, Class>... types) {
+ if (r instanceof Membership membership) {
+ return this.isA(membership.getMemberElement(), types);
+ }
+ return false;
+ }
+
+ private boolean isA(Element e, Class>... types) {
+ if (e == null) {
+ return false;
+ } else {
+ return Stream.of(types).filter(t -> t.isInstance(e)).findAny().isPresent();
+ }
+ }
+
+ private boolean isExtendedUsage(Relationship r) {
+ return r instanceof Membership membership
+ && membership.getMemberElement() != null
+ && membership.getMemberElement().eClass() == SysmlPackage.eINSTANCE.getUsage();
+ }
+}
diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/util/ElementUtil.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/util/ElementUtil.java
new file mode 100644
index 000000000..a25dc1d25
--- /dev/null
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/util/ElementUtil.java
@@ -0,0 +1,396 @@
+/*******************************************************************************
+ * Copyright (c) 2024, 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.sysml.metamodel.util;
+
+import com.fasterxml.uuid.Generators;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.UUID;
+
+import org.eclipse.emf.common.util.TreeIterator;
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.emf.ecore.EAnnotation;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EcoreFactory;
+import org.eclipse.emf.ecore.resource.Resource;
+import org.eclipse.emf.ecore.util.EcoreUtil;
+import org.eclipse.syson.sysml.Element;
+import org.eclipse.syson.sysml.Import;
+import org.eclipse.syson.sysml.LibraryPackage;
+import org.eclipse.syson.sysml.Membership;
+import org.eclipse.syson.sysml.Namespace;
+import org.eclipse.syson.sysml.Specialization;
+import org.eclipse.syson.sysml.SysmlPackage;
+import org.eclipse.syson.sysml.TypeFeaturing;
+import org.eclipse.syson.sysml.VisibilityKind;
+
+/**
+ * Util class for SysML elements.
+ *
+ * @author arichard
+ */
+public class ElementUtil {
+
+ /**
+ * Scheme for URI locating standard KerML libraries.
+ */
+ public static final String KERML_LIBRARY_SCHEME = "kermllibrary";
+
+ /**
+ * Scheme for URI locating standard SysML libraries.
+ */
+ public static final String SYSML_LIBRARY_SCHEME = "sysmllibrary";
+
+ /**
+ * The UUID for NameSpace_URL as required in SysMLv2 specification.
+ */
+ public static final UUID NAME_SPACE_URL_UUID = UUID.fromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8");
+
+ /**
+ * The prefix to prepend to all names for the construction of UUIDs as required in KerML specification.
+ */
+ public static final String KERML_LIBRARY_BASE_URI = "https://www.omg.org/spec/KerML/";
+
+ /**
+ * The prefix to prepend to all names for the construction of UUIDs as required in SysMLv2 specification.
+ */
+ public static final String SYSML_LIBRARY_BASE_URI = "https://www.omg.org/spec/SysML/";
+
+ /**
+ * The source of the {@link EAnnotation} used to flag imported {@link Resource}s.
+ */
+ private static final String IMPORTED_EANNOTATION_SOURCE = "org.eclipse.syson.sysml.imported";
+
+ /**
+ * Checks if the given resource is one of KerML or SysML standard libraries.
+ *
+ * @param resource
+ * a {@link Resource}
+ * @return true is standard resource, false otherwise.
+ */
+ public static boolean isStandardLibraryResource(Resource resource) {
+ if (resource != null && resource.getURI() != null) {
+ URI uri = resource.getURI();
+ return uri.toString().startsWith(KERML_LIBRARY_SCHEME) || uri.toString().startsWith(SYSML_LIBRARY_SCHEME);
+ }
+ return false;
+ }
+
+ /**
+ * Check if the given {@link Element} comes from a standard library (i.e. a {@link LibraryPackage} with its standard
+ * attribute set to true) or not.
+ *
+ * @param element
+ * the given {@link Element}.
+ * @return true if the given element is contained in a standard library, false otherwise.
+ */
+ public static boolean isFromStandardLibrary(Element element) {
+ return element.libraryNamespace() instanceof LibraryPackage libraryPackage
+ && libraryPackage.isIsStandard();
+ }
+
+ /**
+ * Generate a UUID (a v5 for standard library elements a a random v4 random for others).
+ *
+ * @return a UUID.
+ */
+ public static UUID generateUUID(Element element) {
+ UUID uuid = UUID.randomUUID();
+ if (element instanceof LibraryPackage libraryPackage && libraryPackage.isIsStandard()) {
+ Resource resource = element.eResource();
+ if (resource != null) {
+ String uri = ElementUtil.SYSML_LIBRARY_BASE_URI;
+ if (resource.getURI().toString().startsWith("kermllibrary:")) {
+ uri = ElementUtil.KERML_LIBRARY_BASE_URI;
+ }
+ String qualifiedName = element.getQualifiedName();
+ if (qualifiedName != null) {
+ uuid = generateUUIDv5(ElementUtil.NAME_SPACE_URL_UUID, uri + qualifiedName);
+ }
+ }
+ } else if (isFromStandardLibrary(element) && element.getDeclaredName() != null && !noNeedForUUID(element)) {
+ String qualifiedName = element.getQualifiedName();
+ Namespace libraryNamespace = element.libraryNamespace();
+ if (qualifiedName != null && libraryNamespace != null) {
+ UUID namespaceUUID = UUID.fromString(libraryNamespace.getElementId());
+ uuid = ElementUtil.generateUUIDv5(namespaceUUID, qualifiedName);
+ }
+ }
+ return uuid;
+ }
+
+ private static boolean noNeedForUUID(Element element) {
+ return element instanceof Import || element instanceof Membership || element instanceof Specialization || element instanceof TypeFeaturing;
+ }
+
+ /**
+ * Generate a UUID v5 from a given namespace and a value.
+ *
+ * @param namespaceUUID
+ * the namespace to use to generate the UUID.
+ * @param value
+ * the value (i.e. the qualified name of a SysML element) for which we want to generate the UUID.
+ * @return a UUID in version 5.
+ */
+ public static UUID generateUUIDv5(UUID namespaceUUID, String value) {
+ return Generators.nameBasedGenerator(namespaceUUID, null).generate(value);
+ }
+
+ /**
+ * Sets the provided {@code resource} with the provided {@code isImported} flag.
+ *
+ * An imported {@link Resource} returns {@code true} when calling {@code ElementUtil.isImported(resource)}.
+ *
+ *
+ * @param resource
+ * the {@link Resource} to set as imported
+ * @param isImported
+ * the imported flag to set
+ */
+ public static void setIsImported(Resource resource, boolean isImported) {
+ resource.getContents().forEach(eObject -> {
+ if (eObject instanceof Element element) {
+ if (isImported) {
+ if (element.getEAnnotation(IMPORTED_EANNOTATION_SOURCE) == null) {
+ EAnnotation importedEAnnotation = EcoreFactory.eINSTANCE.createEAnnotation();
+ importedEAnnotation.setSource(IMPORTED_EANNOTATION_SOURCE);
+ element.getEAnnotations().add(importedEAnnotation);
+ }
+ } else {
+ if (element.getEAnnotation(IMPORTED_EANNOTATION_SOURCE) != null) {
+ EcoreUtil.remove(element.getEAnnotation(IMPORTED_EANNOTATION_SOURCE));
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Returns {@code true} if the provided {@code resource} is imported.
+ *
+ * @param resource
+ * the {@link Resource} to check
+ * @return {@code true} if the provided {@code resource} is imported
+ */
+ public static boolean isImported(Resource resource) {
+ return resource.getContents().stream()
+ .anyMatch(eObject -> eObject instanceof Element element && element.getEAnnotation(IMPORTED_EANNOTATION_SOURCE) != null);
+ }
+
+ /**
+ * Find an {@link Element} that match the given name and type in the ResourceSet of the given element.
+ *
+ * @param object
+ * the object for which to find a corresponding type.
+ * @param elementName
+ * the element name to match.
+ * @param elementType
+ * the type to match.
+ * @return the found element or null.
+ */
+ public T findByNameAndType(EObject object, String elementName, Class elementType) {
+ final T result = this.findByNameAndType(this.getAllRootsInResourceSet(object), elementName, elementType);
+ return result;
+ }
+
+ /**
+ * Iterate over the given {@link Collection} of root elements to find a element with the given name and type.
+ *
+ * @param roots
+ * the elements to inspect.
+ * @param elementName
+ * the name to match.
+ * @param elementType
+ * the type to match.
+ * @return the found element or null.
+ */
+ public T findByNameAndType(Collection roots, String elementName, Class elementType) {
+ String[] splitElementName = elementName.split("::");
+ List qualifiedName = Arrays.asList(splitElementName);
+ for (final EObject root : roots) {
+ final T result;
+ if (qualifiedName.size() > 1) {
+ result = this.findInRootByQualifiedNameAndTypeFrom(root, qualifiedName, elementType);
+ } else {
+ result = this.findByNameAndTypeFrom(root, elementName, elementType);
+ }
+ if (result != null) {
+ return result;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Count the number of existing ViewUsages inside the given Namespace.
+ *
+ * @param namespace
+ * the given {@link Namespace}.
+ * @return the number of existing ViewUsages inside the given Namespace.
+ */
+ public long existingViewUsagesCountForRepresentationCreation(Namespace namespace) {
+ return namespace.getOwnedMember().stream()
+ .filter(member -> SysmlPackage.eINSTANCE.getViewUsage().equals(member.eClass()))
+ .count();
+ }
+
+ /**
+ * Iterate over the children of the given root {@link EObject} to find an {@link Element} with the given qualified
+ * name and type.
+ *
+ * @param root
+ * the root object to iterate.
+ * @param qualifiedName
+ * the qualified name to match.
+ * @param elementType
+ * the type to match.
+ * @return the found element or null.
+ */
+ private T findInRootByQualifiedNameAndTypeFrom(EObject root, List qualifiedName, Class elementType) {
+ T element = null;
+ if (root instanceof Namespace namespace && namespace.eContainer() == null && namespace.getName() == null) {
+ // Ignore top-level namespaces with no name, they aren't part of the qualified name
+ element = this.findByQualifiedNameAndTypeFrom(namespace, qualifiedName, elementType);
+ } else if (root instanceof Element rootElt && this.nameMatches(rootElt, qualifiedName.get(0))) {
+ element = this.findByQualifiedNameAndTypeFrom(rootElt, qualifiedName.subList(1, qualifiedName.size()), elementType);
+ }
+ return element;
+ }
+
+ /**
+ * Iterate over the children of the given parent {@link EObject} to find an {@link Element} with the given qualified
+ * name and type.
+ *
+ * @param parent
+ * the parent object to iterate.
+ * @param qualifiedName
+ * the qualified name to match.
+ * @param elementType
+ * the type to match.
+ * @return the found element or null.
+ */
+ private T findByQualifiedNameAndTypeFrom(Element parent, List qualifiedName, Class elementType) {
+ T element = null;
+
+ Optional child = parent.getOwnedElement().stream()
+ .filter(Element.class::isInstance)
+ .map(Element.class::cast)
+ .filter(elt -> this.nameMatches(elt, qualifiedName.get(0)))
+ .findFirst();
+ if (child.isEmpty() && parent instanceof Namespace parentNamespace) {
+ // If the element is not owned by the parent it can be visible through a public import.
+ child = parentNamespace.getImportedMembership().stream()
+ .filter(membership -> Objects.equals(membership.getVisibility(), VisibilityKind.PUBLIC))
+ .flatMap(membership -> membership.getRelatedElement().stream())
+ .filter(elt -> this.nameMatches(elt, qualifiedName.get(0)))
+ .findFirst();
+ }
+ if (child.isPresent() && qualifiedName.size() > 1) {
+ element = this.findByQualifiedNameAndTypeFrom(child.get(), qualifiedName.subList(1, qualifiedName.size()), elementType);
+ } else if (child.isPresent() && elementType.isInstance(child.get())) {
+ element = elementType.cast(child.get());
+ }
+ return element;
+ }
+
+ /**
+ * Iterate over the children of the given root {@link EObject} to find an {@link Element} with the given name and
+ * type.
+ *
+ * @param root
+ * the root object to iterate.
+ * @param elementName
+ * the name to match.
+ * @param elementType
+ * the type to match.
+ * @return the found element or null.
+ */
+ private T findByNameAndTypeFrom(EObject root, String elementName, Class elementType) {
+ T element = null;
+
+ if (elementType.isInstance(root) && this.nameMatches(elementType.cast(root), elementName)) {
+ return elementType.cast(root);
+ }
+
+ TreeIterator eAllContents = root.eAllContents();
+ while (eAllContents.hasNext()) {
+ EObject obj = eAllContents.next();
+ if (elementType.isInstance(obj) && this.nameMatches(elementType.cast(obj), elementName)) {
+ element = elementType.cast(obj);
+ break;
+ }
+ }
+
+ return element;
+ }
+
+ /**
+ * Retrieves all the root elements of the resource in the resource set of the given context object.
+ *
+ * @param context
+ * the context object on which to execute this service.
+ * @return a {@link Collection} of all the root element of the current resource set.
+ */
+ private Collection getAllRootsInResourceSet(EObject context) {
+ final Resource res = context.eResource();
+ if (res != null && res.getResourceSet() != null) {
+ final Collection roots = new ArrayList<>();
+ for (final Resource childRes : res.getResourceSet().getResources()) {
+ roots.addAll(childRes.getContents());
+ }
+ return roots;
+ }
+ return Collections.emptyList();
+ }
+
+ /**
+ * Check if the given element's name match the given String.
+ * If there is no match with the name, then the short name is checked.
+ *
+ * @param element
+ * the {@link Element} to check.
+ * @param name
+ * the name to match.
+ * @return true if a match is found, false otherwise.
+ */
+ private boolean nameMatches(Element element, String name) {
+ boolean nameMatches = false;
+ if (element == null || name == null) {
+ nameMatches = false;
+ } else if (this.equalsConsideringOptionalQuotes(element.getName(), name)) {
+ nameMatches = true;
+ } else {
+ nameMatches = this.equalsConsideringOptionalQuotes(element.getShortName(), name);
+ }
+ return nameMatches;
+ }
+
+ private boolean equalsConsideringOptionalQuotes(String candidate, String query) {
+ if (candidate == null || query == null) {
+ return false;
+ }
+ boolean matches = candidate.strip().equals(query.strip());
+ if (!matches && query.startsWith("'") && query.endsWith("'")) {
+ // We give the option to quote names, but the quotes aren't part of the model.
+ String tmpCandidate = "'" + candidate + "'";
+ matches = tmpCandidate.strip().equals(query.strip());
+ }
+ return matches;
+ }
+}
diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/util/RedefinesGenerator.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/util/RedefinesGenerator.java
similarity index 96%
rename from backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/util/RedefinesGenerator.java
rename to backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/util/RedefinesGenerator.java
index adf9ff582..2a8756cf2 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/util/RedefinesGenerator.java
+++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/util/RedefinesGenerator.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.util;
+package org.eclipse.syson.sysml.metamodel.util;
import java.io.IOException;
import java.nio.file.Files;
@@ -56,11 +56,8 @@ public class RedefinesGenerator {
private final String outputDirectory;
- private final String basePackage;
-
- public RedefinesGenerator(String outputDirectory, String basePackage) {
+ public RedefinesGenerator(String outputDirectory) {
this.outputDirectory = outputDirectory;
- this.basePackage = basePackage;
}
/**
@@ -91,7 +88,7 @@ public static void main(String[] args) {
Resource viewResource = resourceSet.getResource(sysmlURI, true);
List allViewContent = new ArrayList<>(viewResource.getContents());
- var gen = new RedefinesGenerator(args[1], args[2]);
+ var gen = new RedefinesGenerator(args[1]);
StreamSupport.stream(Spliterators.spliterator(allViewContent, Spliterator.ORDERED), false).filter(GenModel.class::isInstance).map(GenModel.class::cast).forEach(model -> {
try {
@@ -114,10 +111,10 @@ public void doGen(GenModel model) throws IOException {
}
}
- private StringBuilder updatedContentWithSubsetsRedefines(GenClass clazz, String outputDirectory, String fileName) throws IOException {
+ private StringBuilder updatedContentWithSubsetsRedefines(GenClass clazz, String outputDir, String fileName) throws IOException {
StringBuilder body = new StringBuilder();
- if (Files.exists(Path.of(outputDirectory, fileName))) {
- body.append(Files.readString(Path.of(outputDirectory, fileName)));
+ if (Files.exists(Path.of(outputDir, fileName))) {
+ body.append(Files.readString(Path.of(outputDir, fileName)));
}
body.replace(body.lastIndexOf("}"), body.length(), "");
for (GenFeature feat : clazz.getGenFeatures()) {
@@ -362,7 +359,6 @@ private String genGetRedefinedFeatureBody(GenFeature genFeature, EReference rede
if (redefiningFeatureType.startsWith("EList<")) {
addOrAddAll = "addAll";
}
- String getRedefinedFeatureName = this.genGetRedefinedFeatureName(redefinedFeature);
String packageRedefinedFeature = this.getPackageRedefinedFeature(redefinedFeature);
body.append("""
#redefinedFeatureType #listName = new BasicEList<>();
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/helper/NameHelperTest.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/helper/NameHelperTest.java
similarity index 75%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/helper/NameHelperTest.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/helper/NameHelperTest.java
index 5a0368a3b..8e05d3f71 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/helper/NameHelperTest.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/helper/NameHelperTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.helper;
+package org.eclipse.syson.sysml.metamodel.helper;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -27,13 +27,14 @@ public class NameHelperTest {
@DisplayName("Test that names containing simple quotes are printed with these simple quotes escpaed.")
@Test
void testEscapeSimpleQuotes() {
- String printableName = NameHelper.toPrintableName("Hello what's up!", true);
+ var nameHelper = new NameHelper();
+ String printableName = nameHelper.toPrintableName("Hello what's up!", true);
assertEquals("'Hello what\\'s up!'", printableName);
- printableName = NameHelper.toPrintableName("Hello what's up!", false);
+ printableName = nameHelper.toPrintableName("Hello what's up!", false);
assertEquals("'Hello what's up!'", printableName);
- printableName = NameHelper.toPrintableName("Hel'lo", true);
+ printableName = nameHelper.toPrintableName("Hel'lo", true);
assertEquals("'Hel\\'lo'", printableName);
- printableName = NameHelper.toPrintableName("Hel'lo", false);
+ printableName = nameHelper.toPrintableName("Hel'lo", false);
assertEquals("'Hel'lo'", printableName);
}
}
diff --git a/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/ElementInitializerSwitchTest.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/ElementInitializerSwitchTest.java
index 97f12bfe7..60714315f 100644
--- a/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/ElementInitializerSwitchTest.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/ElementInitializerSwitchTest.java
@@ -27,7 +27,7 @@
import org.eclipse.syson.sysml.SysmlFactory;
import org.eclipse.syson.sysml.SysmlPackage;
import org.eclipse.syson.sysml.VisibilityKind;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/FileNameDeresolverTest.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/FileNameDeresolverTest.java
similarity index 99%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/FileNameDeresolverTest.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/FileNameDeresolverTest.java
index f292b8545..5c94bf768 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/FileNameDeresolverTest.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/FileNameDeresolverTest.java
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual;
+package org.eclipse.syson.sysml.metamodel.services.textual;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -26,7 +26,7 @@
import org.eclipse.syson.sysml.PerformActionUsage;
import org.eclipse.syson.sysml.VerificationCaseUsage;
import org.eclipse.syson.sysml.VisibilityKind;
-import org.eclipse.syson.sysml.textual.utils.FileNameDeresolver;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.FileNameDeresolver;
import org.eclipse.syson.sysml.util.ModelBuilder;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AssertConstraintUsageWithOperatorExpressionTestModel.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AssertConstraintUsageWithOperatorExpressionTestModel.java
similarity index 98%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AssertConstraintUsageWithOperatorExpressionTestModel.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AssertConstraintUsageWithOperatorExpressionTestModel.java
index 4e499a39f..a2acb8b47 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AssertConstraintUsageWithOperatorExpressionTestModel.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AssertConstraintUsageWithOperatorExpressionTestModel.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.models;
+package org.eclipse.syson.sysml.metamodel.services.textual.models;
import org.eclipse.syson.sysml.AssertConstraintUsage;
import org.eclipse.syson.sysml.Expression;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithBinaryOperatorExpressionTestModel.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithBinaryOperatorExpressionTestModel.java
similarity index 97%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithBinaryOperatorExpressionTestModel.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithBinaryOperatorExpressionTestModel.java
index dc46138f4..d67bfb8fe 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithBinaryOperatorExpressionTestModel.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithBinaryOperatorExpressionTestModel.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.models;
+package org.eclipse.syson.sysml.metamodel.services.textual.models;
import org.eclipse.syson.sysml.AttributeUsage;
import org.eclipse.syson.sysml.Feature;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithBracketOperatorExpressionTestModel.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithBracketOperatorExpressionTestModel.java
similarity index 97%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithBracketOperatorExpressionTestModel.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithBracketOperatorExpressionTestModel.java
index 6ef3da728..f5950f7ca 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithBracketOperatorExpressionTestModel.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithBracketOperatorExpressionTestModel.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.models;
+package org.eclipse.syson.sysml.metamodel.services.textual.models;
import org.eclipse.syson.sysml.AttributeUsage;
import org.eclipse.syson.sysml.Feature;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithFeatureChainExpressionTestModel.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithFeatureChainExpressionTestModel.java
similarity index 98%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithFeatureChainExpressionTestModel.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithFeatureChainExpressionTestModel.java
index 3a59399b5..ad7e2518d 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithFeatureChainExpressionTestModel.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithFeatureChainExpressionTestModel.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.models;
+package org.eclipse.syson.sysml.metamodel.services.textual.models;
import org.eclipse.syson.sysml.AttributeUsage;
import org.eclipse.syson.sysml.Feature;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithInvocationExpressionTestModel.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithInvocationExpressionTestModel.java
similarity index 96%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithInvocationExpressionTestModel.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithInvocationExpressionTestModel.java
index 6a0154f95..02dab778d 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithInvocationExpressionTestModel.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithInvocationExpressionTestModel.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.models;
+package org.eclipse.syson.sysml.metamodel.services.textual.models;
import org.eclipse.syson.sysml.AttributeUsage;
import org.eclipse.syson.sysml.Feature;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithSequenceExpressionTestModel.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithSequenceExpressionTestModel.java
similarity index 98%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithSequenceExpressionTestModel.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithSequenceExpressionTestModel.java
index f56e0a57d..32cef4471 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/AttributeUsageWithSequenceExpressionTestModel.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/AttributeUsageWithSequenceExpressionTestModel.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.models;
+package org.eclipse.syson.sysml.metamodel.services.textual.models;
import org.eclipse.syson.sysml.AttributeUsage;
import org.eclipse.syson.sysml.Feature;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/ConditionalBinaryOperatorExpressionTestModel.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/ConditionalBinaryOperatorExpressionTestModel.java
similarity index 97%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/ConditionalBinaryOperatorExpressionTestModel.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/ConditionalBinaryOperatorExpressionTestModel.java
index da51db30e..1ffd49109 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/ConditionalBinaryOperatorExpressionTestModel.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/ConditionalBinaryOperatorExpressionTestModel.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.models;
+package org.eclipse.syson.sysml.metamodel.services.textual.models;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.Expression;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/ConstraintUsageWithOperatorExpressionTestModel.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/ConstraintUsageWithOperatorExpressionTestModel.java
similarity index 98%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/ConstraintUsageWithOperatorExpressionTestModel.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/ConstraintUsageWithOperatorExpressionTestModel.java
index 8eee17c18..8a864676f 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/ConstraintUsageWithOperatorExpressionTestModel.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/ConstraintUsageWithOperatorExpressionTestModel.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.models;
+package org.eclipse.syson.sysml.metamodel.services.textual.models;
import org.eclipse.syson.sysml.ConstraintUsage;
import org.eclipse.syson.sysml.Expression;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/IsImplicitTest.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/IsImplicitTest.java
similarity index 97%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/IsImplicitTest.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/IsImplicitTest.java
index 498de1568..75b22aeac 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/IsImplicitTest.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/IsImplicitTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.models;
+package org.eclipse.syson.sysml.metamodel.services.textual.models;
import org.eclipse.syson.sysml.ObjectiveMembership;
import org.eclipse.syson.sysml.Redefinition;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/UseCaseDefinitionTestModel.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/UseCaseDefinitionTestModel.java
similarity index 97%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/UseCaseDefinitionTestModel.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/UseCaseDefinitionTestModel.java
index de24d6731..d9ebe0009 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/UseCaseDefinitionTestModel.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/UseCaseDefinitionTestModel.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.models;
+package org.eclipse.syson.sysml.metamodel.services.textual.models;
import org.eclipse.syson.sysml.ActorMembership;
import org.eclipse.syson.sysml.ObjectiveMembership;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/sample/CameraModel.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/sample/CameraModel.java
similarity index 98%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/sample/CameraModel.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/sample/CameraModel.java
index 3a1ca7a2e..09e5f857a 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/sample/CameraModel.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/sample/CameraModel.java
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.models.sample;
+package org.eclipse.syson.sysml.metamodel.services.textual.models.sample;
import org.eclipse.syson.sysml.Feature;
import org.eclipse.syson.sysml.NamespaceImport;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/sample/ItemTest.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/sample/ItemTest.java
similarity index 98%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/sample/ItemTest.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/sample/ItemTest.java
index 04eb1e804..90c65031b 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/sample/ItemTest.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/sample/ItemTest.java
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.models.sample;
+package org.eclipse.syson.sysml.metamodel.services.textual.models.sample;
import org.eclipse.syson.sysml.FeatureDirectionKind;
import org.eclipse.syson.sysml.ItemDefinition;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/sample/PictureTakingModel.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/sample/PictureTakingModel.java
similarity index 97%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/sample/PictureTakingModel.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/sample/PictureTakingModel.java
index b0c5181d0..15e72fb54 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/textual/models/sample/PictureTakingModel.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/services/textual/models/sample/PictureTakingModel.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.textual.models.sample;
+package org.eclipse.syson.sysml.metamodel.services.textual.models.sample;
import org.eclipse.syson.sysml.ActionDefinition;
import org.eclipse.syson.sysml.ActionUsage;
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/metamodel/ElementUtilTest.java b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/util/ElementUtilTest.java
similarity index 96%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/metamodel/ElementUtilTest.java
rename to backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/util/ElementUtilTest.java
index a8b451555..e72f19b41 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/metamodel/ElementUtilTest.java
+++ b/backend/services/syson-sysml-metamodel-services/src/test/java/org/eclipse/syson/sysml/metamodel/util/ElementUtilTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
-package org.eclipse.syson.sysml.metamodel;
+package org.eclipse.syson.sysml.metamodel.util;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -22,7 +22,6 @@
import org.eclipse.syson.sysml.LibraryPackage;
import org.eclipse.syson.sysml.OwningMembership;
import org.eclipse.syson.sysml.SysmlFactory;
-import org.eclipse.syson.sysml.util.ElementUtil;
import org.eclipse.syson.sysml.util.SysmlResourceImpl;
import org.junit.jupiter.api.Test;
diff --git a/backend/services/syson-sysml-rest-api-services/src/main/java/org/eclipse/syson/sysml/rest/api/SysMLv2JsonSerializer.java b/backend/services/syson-sysml-rest-api-services/src/main/java/org/eclipse/syson/sysml/rest/api/SysMLv2JsonSerializer.java
index 32c8445b0..f76ba12db 100644
--- a/backend/services/syson-sysml-rest-api-services/src/main/java/org/eclipse/syson/sysml/rest/api/SysMLv2JsonSerializer.java
+++ b/backend/services/syson-sysml-rest-api-services/src/main/java/org/eclipse/syson/sysml/rest/api/SysMLv2JsonSerializer.java
@@ -24,7 +24,7 @@
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.sirius.components.emf.services.EObjectIDManager;
import org.eclipse.syson.sysml.Element;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.eclipse.syson.sysml.util.VirtualLinkAdapter;
import tools.jackson.core.JacksonException;
diff --git a/backend/services/syson-sysml-rest-api-services/src/main/java/org/eclipse/syson/sysml/rest/api/SysONObjectRestService.java b/backend/services/syson-sysml-rest-api-services/src/main/java/org/eclipse/syson/sysml/rest/api/SysONObjectRestService.java
index 16cf8e458..b36b775df 100644
--- a/backend/services/syson-sysml-rest-api-services/src/main/java/org/eclipse/syson/sysml/rest/api/SysONObjectRestService.java
+++ b/backend/services/syson-sysml-rest-api-services/src/main/java/org/eclipse/syson/sysml/rest/api/SysONObjectRestService.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024, 2025 Obeo.
+ * Copyright (c) 2024, 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
@@ -32,7 +32,7 @@
import org.eclipse.syson.services.UtilService;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.Relationship;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.springframework.stereotype.Service;
/**
diff --git a/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONDefaultExplorerServices.java b/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONDefaultExplorerServices.java
index 906e39d7b..1f63e0aff 100644
--- a/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONDefaultExplorerServices.java
+++ b/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONDefaultExplorerServices.java
@@ -42,10 +42,10 @@
import org.eclipse.syson.sysml.Type;
import org.eclipse.syson.sysml.ViewUsage;
import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService;
-import org.eclipse.syson.sysml.textual.SysMLElementSerializer;
-import org.eclipse.syson.sysml.textual.SysMLSerializingOptions;
-import org.eclipse.syson.sysml.textual.utils.FileNameDeresolver;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.services.textual.SysMLElementSerializer;
+import org.eclipse.syson.sysml.metamodel.services.textual.SysMLSerializingOptions;
+import org.eclipse.syson.sysml.metamodel.services.textual.utils.FileNameDeresolver;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.eclipse.syson.tree.explorer.fragments.KerMLStandardLibraryDirectory;
import org.eclipse.syson.tree.explorer.fragments.LibrariesDirectory;
import org.eclipse.syson.tree.explorer.fragments.SysMLStandardLibraryDirectory;
diff --git a/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONExplorerFilterService.java b/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONExplorerFilterService.java
index d4bb946de..07795b306 100644
--- a/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONExplorerFilterService.java
+++ b/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONExplorerFilterService.java
@@ -26,7 +26,7 @@
import org.eclipse.syson.sysml.Membership;
import org.eclipse.syson.sysml.Namespace;
import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.eclipse.syson.tree.explorer.filters.SysONTreeFilterConstants;
import org.eclipse.syson.tree.explorer.services.api.ISysONExplorerFilterService;
import org.springframework.stereotype.Service;
diff --git a/backend/services/syson-tree-services/src/test/java/org/eclipse/syson/tree/explorer/services/SysONExplorerFilterServiceTest.java b/backend/services/syson-tree-services/src/test/java/org/eclipse/syson/tree/explorer/services/SysONExplorerFilterServiceTest.java
index 7d2ca4ba0..4bbd9216c 100644
--- a/backend/services/syson-tree-services/src/test/java/org/eclipse/syson/tree/explorer/services/SysONExplorerFilterServiceTest.java
+++ b/backend/services/syson-tree-services/src/test/java/org/eclipse/syson/tree/explorer/services/SysONExplorerFilterServiceTest.java
@@ -29,7 +29,7 @@
import org.eclipse.syson.sysml.Namespace;
import org.eclipse.syson.sysml.PartUsage;
import org.eclipse.syson.sysml.SysmlFactory;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.eclipse.syson.tree.explorer.services.api.ISysONExplorerFilterService;
import org.junit.jupiter.api.Test;
diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractAllocateEdgeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractAllocateEdgeDescriptionProvider.java
index d5dc33fa7..de5d1d818 100644
--- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractAllocateEdgeDescriptionProvider.java
+++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/edges/AbstractAllocateEdgeDescriptionProvider.java
@@ -26,7 +26,7 @@
import org.eclipse.sirius.components.view.diagram.SynchronizationPolicy;
import org.eclipse.syson.diagram.common.view.services.ViewEdgeService;
import org.eclipse.syson.sysml.SysmlPackage;
-import org.eclipse.syson.sysml.helper.LabelConstants;
+import org.eclipse.syson.sysml.metamodel.helper.LabelConstants;
import org.eclipse.syson.util.AQLConstants;
import org.eclipse.syson.util.ServiceMethod;
import org.eclipse.syson.util.SysMLMetamodelHelper;
diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewCreateService.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewCreateService.java
index 8bb066b2e..d4582e6b3 100644
--- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewCreateService.java
+++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewCreateService.java
@@ -85,7 +85,7 @@
import org.eclipse.syson.sysml.UseCaseUsage;
import org.eclipse.syson.sysml.metamodel.services.ElementInitializerSwitch;
import org.eclipse.syson.sysml.metamodel.services.MetamodelMutationElementService;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.eclipse.syson.util.NodeFinder;
/**
diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewLabelService.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewLabelService.java
index 90723a381..1101fc00d 100644
--- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewLabelService.java
+++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/ViewLabelService.java
@@ -20,8 +20,8 @@
import org.eclipse.sirius.components.representations.MessageLevel;
import org.eclipse.syson.services.SimpleNameDeresolver;
import org.eclipse.syson.sysml.Element;
-import org.eclipse.syson.sysml.textual.SysMLElementSerializer;
-import org.eclipse.syson.sysml.textual.SysMLSerializingOptions;
+import org.eclipse.syson.sysml.metamodel.services.textual.SysMLElementSerializer;
+import org.eclipse.syson.sysml.metamodel.services.textual.SysMLSerializingOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/backend/views/syson-diagram-tests/src/main/java/org/eclipse/syson/diagram/tests/checkers/AQLExpressionCallsExistingServicesChecker.java b/backend/views/syson-diagram-tests/src/main/java/org/eclipse/syson/diagram/tests/checkers/AQLExpressionCallsExistingServicesChecker.java
index e33824fd0..76e29de02 100644
--- a/backend/views/syson-diagram-tests/src/main/java/org/eclipse/syson/diagram/tests/checkers/AQLExpressionCallsExistingServicesChecker.java
+++ b/backend/views/syson-diagram-tests/src/main/java/org/eclipse/syson/diagram/tests/checkers/AQLExpressionCallsExistingServicesChecker.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024 Obeo.
+ * Copyright (c) 2024, 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
@@ -27,7 +27,7 @@
import org.eclipse.acceleo.query.runtime.Query;
import org.eclipse.acceleo.query.runtime.QueryParsing;
import org.eclipse.sirius.components.interpreter.SimpleCrossReferenceProvider;
-import org.eclipse.syson.sysml.helper.EMFUtils;
+import org.eclipse.syson.sysml.metamodel.helper.EMFUtils;
/**
* Checks that the provided AQL expression contains services that are either default services or diagram services.
diff --git a/backend/views/syson-diagram-tests/src/main/java/org/eclipse/syson/diagram/tests/checkers/NodeDescriptionIsReusedByChecker.java b/backend/views/syson-diagram-tests/src/main/java/org/eclipse/syson/diagram/tests/checkers/NodeDescriptionIsReusedByChecker.java
index bfda5500d..700e7d80e 100644
--- a/backend/views/syson-diagram-tests/src/main/java/org/eclipse/syson/diagram/tests/checkers/NodeDescriptionIsReusedByChecker.java
+++ b/backend/views/syson-diagram-tests/src/main/java/org/eclipse/syson/diagram/tests/checkers/NodeDescriptionIsReusedByChecker.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2024 Obeo.
+ * Copyright (c) 2024, 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
@@ -19,7 +19,7 @@
import org.eclipse.sirius.components.view.diagram.DiagramDescription;
import org.eclipse.sirius.components.view.diagram.NodeDescription;
-import org.eclipse.syson.sysml.helper.EMFUtils;
+import org.eclipse.syson.sysml.metamodel.helper.EMFUtils;
/**
* Checks that the provided {@link NodeDescription} is reused by the expected elements.
diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/FrameConcernEdgeDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/FrameConcernEdgeDescriptionProvider.java
index eeca4f992..602ef2a0f 100644
--- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/FrameConcernEdgeDescriptionProvider.java
+++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/FrameConcernEdgeDescriptionProvider.java
@@ -33,7 +33,7 @@
import org.eclipse.syson.services.DeleteService;
import org.eclipse.syson.services.UtilService;
import org.eclipse.syson.sysml.SysmlPackage;
-import org.eclipse.syson.sysml.helper.LabelConstants;
+import org.eclipse.syson.sysml.metamodel.helper.LabelConstants;
import org.eclipse.syson.util.AQLConstants;
import org.eclipse.syson.util.IDescriptionNameGenerator;
import org.eclipse.syson.util.ServiceMethod;
diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/IncludeUseCaseDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/IncludeUseCaseDescriptionProvider.java
index e8eae7fee..640368ec0 100644
--- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/IncludeUseCaseDescriptionProvider.java
+++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/edges/IncludeUseCaseDescriptionProvider.java
@@ -32,7 +32,7 @@
import org.eclipse.syson.services.UtilService;
import org.eclipse.syson.sysml.IncludeUseCaseUsage;
import org.eclipse.syson.sysml.SysmlPackage;
-import org.eclipse.syson.sysml.helper.LabelConstants;
+import org.eclipse.syson.sysml.metamodel.helper.LabelConstants;
import org.eclipse.syson.util.AQLConstants;
import org.eclipse.syson.util.IDescriptionNameGenerator;
import org.eclipse.syson.util.ServiceMethod;
diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/services/SysONShowHideSDVElementEventHandler.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/services/SysONShowHideSDVElementEventHandler.java
index c14ff107c..660fc9cd2 100644
--- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/services/SysONShowHideSDVElementEventHandler.java
+++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/services/SysONShowHideSDVElementEventHandler.java
@@ -42,7 +42,7 @@
import org.eclipse.sirius.web.domain.boundedcontexts.representationdata.services.api.IRepresentationMetadataSearchService;
import org.eclipse.sirius.web.domain.boundedcontexts.semanticdata.SemanticData;
import org.eclipse.syson.standard.diagrams.view.SDVDescriptionNameGenerator;
-import org.eclipse.syson.sysml.helper.EMFUtils;
+import org.eclipse.syson.sysml.metamodel.helper.EMFUtils;
import org.eclipse.syson.util.NodeFinder;
import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers;
import org.springframework.core.Ordered;
diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/services/nodeactions/managevisibility/ManageVisibilityNodeActionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/services/nodeactions/managevisibility/ManageVisibilityNodeActionProvider.java
index dcf6b6b83..17535bbd7 100644
--- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/services/nodeactions/managevisibility/ManageVisibilityNodeActionProvider.java
+++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/services/nodeactions/managevisibility/ManageVisibilityNodeActionProvider.java
@@ -30,7 +30,7 @@
import org.eclipse.syson.standard.diagrams.view.SDVDiagramDescriptionProvider;
import org.eclipse.syson.sysml.Definition;
import org.eclipse.syson.sysml.Usage;
-import org.eclipse.syson.sysml.helper.EMFUtils;
+import org.eclipse.syson.sysml.metamodel.helper.EMFUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
diff --git a/backend/views/syson-standard-diagrams-view/src/test/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionTests.java b/backend/views/syson-standard-diagrams-view/src/test/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionTests.java
index 5c3a1248a..6ce3f7424 100644
--- a/backend/views/syson-standard-diagrams-view/src/test/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionTests.java
+++ b/backend/views/syson-standard-diagrams-view/src/test/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionTests.java
@@ -42,7 +42,7 @@
import org.eclipse.syson.diagram.tests.predicates.DiagramPredicates;
import org.eclipse.syson.services.ColorProvider;
import org.eclipse.syson.sysml.SysmlPackage;
-import org.eclipse.syson.sysml.helper.EMFUtils;
+import org.eclipse.syson.sysml.metamodel.helper.EMFUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/menu/context/CreateRepresentationInputProcessor.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/menu/context/CreateRepresentationInputProcessor.java
index c3e9a1835..c60d3e191 100644
--- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/menu/context/CreateRepresentationInputProcessor.java
+++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/menu/context/CreateRepresentationInputProcessor.java
@@ -29,7 +29,7 @@
import org.eclipse.syson.sysml.SysmlFactory;
import org.eclipse.syson.sysml.ViewDefinition;
import org.eclipse.syson.sysml.ViewUsage;
-import org.eclipse.syson.sysml.util.ElementUtil;
+import org.eclipse.syson.sysml.metamodel.util.ElementUtil;
import org.eclipse.syson.util.StandardDiagramsConstants;
import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers;
import org.springframework.stereotype.Service;
diff --git a/backend/views/syson-tree-explorer-view/src/test/java/org/eclipse/syson/tree/explorer/view/duplicate/SysMLContainmentMembershipLabelSwitchTest.java b/backend/views/syson-tree-explorer-view/src/test/java/org/eclipse/syson/tree/explorer/view/duplicate/SysMLContainmentMembershipLabelSwitchTest.java
index 8a76b8c46..c36c98bf2 100644
--- a/backend/views/syson-tree-explorer-view/src/test/java/org/eclipse/syson/tree/explorer/view/duplicate/SysMLContainmentMembershipLabelSwitchTest.java
+++ b/backend/views/syson-tree-explorer-view/src/test/java/org/eclipse/syson/tree/explorer/view/duplicate/SysMLContainmentMembershipLabelSwitchTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2025 Obeo.
+ * Copyright (c) 2025, 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
@@ -18,7 +18,7 @@
import org.eclipse.emf.ecore.EClass;
import org.eclipse.syson.sysml.SysmlPackage;
-import org.eclipse.syson.sysml.helper.EMFUtils;
+import org.eclipse.syson.sysml.metamodel.helper.EMFUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
diff --git a/pom.xml b/pom.xml
index 1cadcbf33..bf202440a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,6 +1,6 @@