diff --git a/SysML2.NET.Tests/Extend/ElementExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ElementExtensionsTestFixture.cs index dcfd0bfa..00951b68 100644 --- a/SysML2.NET.Tests/Extend/ElementExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/ElementExtensionsTestFixture.cs @@ -21,78 +21,292 @@ namespace SysML2.NET.Tests.Extend { using System; - + using NUnit.Framework; - + + using SysML2.NET.Core.POCO.Kernel.Packages; + using SysML2.NET.Core.POCO.Root.Annotations; using SysML2.NET.Core.POCO.Root.Elements; + using SysML2.NET.Core.POCO.Root.Namespaces; + using SysML2.NET.Core.POCO.Systems.DefinitionAndUsage; + using SysML2.NET.Extensions; [TestFixture] public class ElementExtensionsTestFixture { [Test] - public void ComputeDocumentation_ThrowsNotSupportedException() + public void VerifyComputeDocumentation() { - Assert.That(() => ((IElement)null).ComputeDocumentation(), Throws.TypeOf()); + Assert.That(() => ((IElement)null).ComputeDocumentation(), Throws.TypeOf()); + + var element = new Definition(); + var documentation = new Documentation(); + var annotation = new Annotation(); + + Assert.That(element.ComputeDocumentation, Has.Count.EqualTo(0)); + + element.AssignOwnership(annotation, documentation); + + Assert.That(element.ComputeDocumentation(), Is.EquivalentTo([documentation])); } [Test] - public void ComputeIsLibraryElement_ThrowsNotSupportedException() + public void VerifyComputeIsLibraryElement() { - Assert.That(() => ((IElement)null).ComputeIsLibraryElement(), Throws.TypeOf()); + Assert.That(() => ((IElement)null).ComputeIsLibraryElement(), Throws.TypeOf()); + + var element = new Definition(); + var documentation = new Documentation(); + var annotation = new Annotation(); + + using (Assert.EnterMultipleScope()) + { + Assert.That(element.ComputeIsLibraryElement, Is.False); + Assert.That(documentation.ComputeIsLibraryElement, Is.False); + } + + element.AssignOwnership(annotation, documentation); + + using (Assert.EnterMultipleScope()) + { + Assert.That(element.ComputeIsLibraryElement, Is.False); + Assert.That(documentation.ComputeIsLibraryElement, Throws.TypeOf()); + } } [Test] - public void ComputeName_ThrowsNotSupportedException() + public void VerifyComputeName() { - Assert.That(() => ((IElement)null).ComputeName(), Throws.TypeOf()); + Assert.That(() => ((IElement)null).ComputeName(), Throws.TypeOf()); + + var element = new Definition(); + + Assert.That(element.ComputeName, Is.Null); + + element.DeclaredName = "definitionName"; + Assert.That(element.ComputeName, Is.EqualTo("definitionName")); } [Test] - public void ComputeOwnedAnnotation_ThrowsNotSupportedException() + public void VerifyComputeOwnedAnnotation() { - Assert.That(() => ((IElement)null).ComputeOwnedAnnotation(), Throws.TypeOf()); + Assert.That(() => ((IElement)null).ComputeOwnedAnnotation(), Throws.TypeOf()); + + var element = new Definition(); + var documentation = new Documentation(); + var annotation = new Annotation(); + element.AssignOwnership(annotation, documentation); + Assert.That(element.ComputeOwnedAnnotation, Has.Count.EqualTo(0)); + + annotation.AnnotatedElement = element; + Assert.That(element.ComputeOwnedAnnotation, Is.EquivalentTo([annotation])); } [Test] - public void ComputeOwnedElement_ThrowsNotSupportedException() + public void VerifyComputeOwnedElement() { - Assert.That(() => ((IElement)null).ComputeOwnedElement(), Throws.TypeOf()); + Assert.That(() => ((IElement)null).ComputeOwnedElement(), Throws.TypeOf()); + + var element = new Definition(); + var documentation = new Documentation(); + var annotation = new Annotation(); + Assert.That(element.ComputeOwnedElement, Has.Count.EqualTo(0)); + + element.AssignOwnership(annotation, documentation); + Assert.That(element.ComputeDocumentation, Is.EquivalentTo([documentation])); } [Test] - public void ComputeOwner_ThrowsNotSupportedException() + public void VerifyComputeOwner() { - Assert.That(() => ((IElement)null).ComputeOwner(), Throws.TypeOf()); + Assert.That(() => ((IElement)null).ComputeOwner(), Throws.TypeOf()); + + var element = new Definition(); + var documentation = new Documentation(); + var annotation = new Annotation(); + + using (Assert.EnterMultipleScope()) + { + Assert.That(element.ComputeOwner, Is.Null); + Assert.That(documentation.ComputeOwner, Is.Null); + } + + element.AssignOwnership(annotation, documentation); + + using (Assert.EnterMultipleScope()) + { + Assert.That(element.ComputeOwner, Is.Null); + Assert.That(documentation.ComputeOwner, Is.EqualTo(element)); + } } [Test] - public void ComputeOwningMembership_ThrowsNotSupportedException() + public void VerifyComputeOwningMembership() { - Assert.That(() => ((IElement)null).ComputeOwningMembership(), Throws.TypeOf()); + Assert.That(() => ((IElement)null).ComputeOwningMembership(), Throws.TypeOf()); + + var element = new Definition(); + var documentation = new Documentation(); + var annotation = new Annotation(); + + Assert.That(documentation.ComputeOwningMembership, Is.Null); + + element.AssignOwnership(annotation, documentation); + + using (Assert.EnterMultipleScope()) + { + Assert.That(documentation.ComputeOwningMembership, Is.Null); + Assert.That(element.ComputeOwningMembership, Is.Null); + } + + var membership = new OwningMembership(); + var namespaceElement = new Namespace(); + + namespaceElement.AssignOwnership(membership, element); + Assert.That(element.ComputeOwningMembership, Is.EqualTo(membership)); } [Test] - public void ComputeOwningNamespace_ThrowsNotSupportedException() + public void VerifyComputeOwningNamespace() { - Assert.That(() => ((IElement)null).ComputeOwningNamespace(), Throws.TypeOf()); + Assert.That(() => ((IElement)null).ComputeOwningNamespace(), Throws.TypeOf()); + + var element = new Definition(); + var documentation = new Documentation(); + var annotation = new Annotation(); + element.AssignOwnership(annotation, documentation); + + Assert.That(documentation.ComputeOwningNamespace, Is.Null); + var membership = new OwningMembership(); + var namespaceElement = new Namespace(); + + namespaceElement.AssignOwnership(membership, element); + Assert.That(element.ComputeOwningNamespace, Is.EqualTo(namespaceElement)); } [Test] - public void ComputeQualifiedName_ThrowsNotSupportedException() + public void VerifyComputeQualifiedName() { - Assert.That(() => ((IElement)null).ComputeQualifiedName(), Throws.TypeOf()); + Assert.That(() => ((IElement)null).ComputeQualifiedName(), Throws.TypeOf()); + + var element = new Definition(); + + Assert.That(element.ComputeQualifiedName, Is.Null); + + var membership = new OwningMembership(); + var secondMembership = new OwningMembership(); + + var namespaceElement = new Namespace(); + + namespaceElement.AssignOwnership(membership, element); + element.DeclaredName = "name"; + + Assert.That(element.ComputeQualifiedName, Is.EqualTo("name")); + + var secondElement = new Definition() + { + DeclaredName = "name" + }; + + namespaceElement.AssignOwnership(secondMembership, secondElement); + + using (Assert.EnterMultipleScope()) + { + Assert.That(element.ComputeQualifiedName, Is.EqualTo("name")); + Assert.That(secondElement.ComputeQualifiedName, Is.Null); + } + + var packageOwner = new Package() + { + DeclaredName = "owner" + }; + + var packageMembership = new OwningMembership(); + packageOwner.AssignOwnership(packageMembership, namespaceElement); + + Assert.That(element.ComputeQualifiedName, Is.Null); + + namespaceElement.DeclaredName = "namespace"; + Assert.That(element.ComputeQualifiedName, Is.EqualTo("namespace::name")); } [Test] - public void ComputeShortName_ThrowsNotSupportedException() + public void VerifyComputeShortName() { - Assert.That(() => ((IElement)null).ComputeShortName(), Throws.TypeOf()); + Assert.That(() => ((IElement)null).ComputeShortName(), Throws.TypeOf()); + + var element = new Definition(); + + Assert.That(element.ComputeShortName, Is.Null); + + element.DeclaredShortName = "shortName"; + Assert.That(element.ComputeShortName, Is.EqualTo("shortName")); } [Test] - public void ComputeTextualRepresentation_ThrowsNotSupportedException() + public void VerifyComputeTextualRepresentation() + { + Assert.That(() => ((IElement)null).ComputeTextualRepresentation(), Throws.TypeOf()); + + var element = new Definition(); + var textualRepresentation = new TextualRepresentation(); + var annotation = new Annotation(); + + Assert.That(element.ComputeTextualRepresentation, Has.Count.EqualTo(0)); + + element.AssignOwnership(annotation, textualRepresentation); + + Assert.That(element.ComputeTextualRepresentation(), Is.EquivalentTo([textualRepresentation])); + } + + [Test] + public void VerifyComputePathOperation() + { + Assert.That(() => ((IElement)null).ComputePathOperation(), Throws.TypeOf()); + + var element = new Definition(); + + Assert.That(element.ComputePathOperation, Is.Empty); + element.DeclaredName = "name"; + + Assert.That(element.ComputePathOperation, Is.Empty); + + var membership = new OwningMembership(); + var secondMembership = new OwningMembership(); + var namespaceElement = new Namespace(); + + namespaceElement.AssignOwnership(membership, element); + + Assert.That(element.ComputePathOperation, Is.EqualTo("name")); + + var secondElement = new Definition() + { + DeclaredName = "name" + }; + + namespaceElement.AssignOwnership(secondMembership, secondElement); + + using (Assert.EnterMultipleScope()) + { + Assert.That(element.ComputePathOperation, Is.EqualTo("name")); + Assert.That(secondElement.ComputePathOperation, Throws.TypeOf()); + } + } + + [Test] + public void VerifyComputeEscapedNameOperation() { - Assert.That(() => ((IElement)null).ComputeTextualRepresentation(), Throws.TypeOf()); + Assert.That(() => ((IElement)null).ComputeEscapedNameOperation(), Throws.TypeOf()); + + var element = new Definition() + { + DeclaredName = "basic" + }; + + Assert.That(element.ComputeEscapedNameOperation, Is.EqualTo("basic")); + + element.DeclaredName = "non basic"; + Assert.That(element.ComputeEscapedNameOperation, Is.EqualTo("\'non basic\'")); } } } diff --git a/SysML2.NET.Tests/Extend/MembershipExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/MembershipExtensionsTestFixture.cs index c10bcf6a..a304b730 100644 --- a/SysML2.NET.Tests/Extend/MembershipExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/MembershipExtensionsTestFixture.cs @@ -38,7 +38,7 @@ public void ComputeMemberElementId_ThrowsNotSupportedException() [Test] public void ComputeMembershipOwningNamespace_ThrowsNotSupportedException() { - Assert.That(() => ((IMembership)null).ComputeMembershipOwningNamespace(), Throws.TypeOf()); + Assert.That(() => ((IMembership)null).ComputeMembershipOwningNamespace(), Throws.TypeOf()); } } } diff --git a/SysML2.NET.Tests/Extend/OwningMembershipExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/OwningMembershipExtensionsTestFixture.cs index 4cc689cb..61b8b449 100644 --- a/SysML2.NET.Tests/Extend/OwningMembershipExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/OwningMembershipExtensionsTestFixture.cs @@ -32,7 +32,7 @@ public class OwningMembershipExtensionsTestFixture [Test] public void ComputeOwnedMemberElement_ThrowsNotSupportedException() { - Assert.That(() => ((IOwningMembership)null).ComputeOwnedMemberElement(), Throws.TypeOf()); + Assert.That(() => ((IOwningMembership)null).ComputeOwnedMemberElement(), Throws.TypeOf()); } [Test] @@ -44,7 +44,7 @@ public void ComputeOwnedMemberElementId_ThrowsNotSupportedException() [Test] public void ComputeOwnedMemberName_ThrowsNotSupportedException() { - Assert.That(() => ((IOwningMembership)null).ComputeOwnedMemberName(), Throws.TypeOf()); + Assert.That(() => ((IOwningMembership)null).ComputeOwnedMemberName(), Throws.TypeOf()); } [Test] diff --git a/SysML2.NET/Extend/ElementExtensions.cs b/SysML2.NET/Extend/ElementExtensions.cs index eeb28540..bc3acc0b 100644 --- a/SysML2.NET/Extend/ElementExtensions.cs +++ b/SysML2.NET/Extend/ElementExtensions.cs @@ -22,9 +22,11 @@ namespace SysML2.NET.Core.POCO.Root.Elements { using System; using System.Collections.Generic; + using System.Linq; using SysML2.NET.Core.POCO.Root.Annotations; using SysML2.NET.Core.POCO.Root.Namespaces; + using SysML2.NET.Extensions; /// /// The class provides extensions methods for @@ -41,10 +43,9 @@ internal static class ElementExtensions /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeDocumentation(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return elementSubject == null ? throw new ArgumentNullException(nameof(elementSubject)) : [..elementSubject.ownedElement.OfType()]; } /// @@ -56,10 +57,9 @@ internal static List ComputeDocumentation(this IElement elementS /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static bool ComputeIsLibraryElement(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return elementSubject == null ? throw new ArgumentNullException(nameof(elementSubject)) : elementSubject.LibraryNamespace() != null; } /// @@ -71,10 +71,9 @@ internal static bool ComputeIsLibraryElement(this IElement elementSubject) /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static string ComputeName(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return elementSubject == null ? throw new ArgumentNullException(nameof(elementSubject)) : elementSubject.EffectiveName(); } /// @@ -86,10 +85,9 @@ internal static string ComputeName(this IElement elementSubject) /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeOwnedAnnotation(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return elementSubject == null ? throw new ArgumentNullException(nameof(elementSubject)) : [..elementSubject.OwnedRelationship.OfType().Where(x => x.AnnotatedElement == elementSubject)]; } /// @@ -101,10 +99,9 @@ internal static List ComputeOwnedAnnotation(this IElement elementSu /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeOwnedElement(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return elementSubject == null ? throw new ArgumentNullException(nameof(elementSubject)) : [..elementSubject.OwnedRelationship.SelectMany(x => x.OwnedRelatedElement)]; } /// @@ -116,10 +113,9 @@ internal static List ComputeOwnedElement(this IElement elementSubject) /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IElement ComputeOwner(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return elementSubject == null ? throw new ArgumentNullException(nameof(elementSubject)) :elementSubject.OwningRelationship?.OwningRelatedElement; } /// @@ -131,10 +127,9 @@ internal static IElement ComputeOwner(this IElement elementSubject) /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IOwningMembership ComputeOwningMembership(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return elementSubject == null ? throw new ArgumentNullException(nameof(elementSubject)) : elementSubject.OwningRelationship as IOwningMembership; } /// @@ -146,10 +141,9 @@ internal static IOwningMembership ComputeOwningMembership(this IElement elementS /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static INamespace ComputeOwningNamespace(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return elementSubject == null ? throw new ArgumentNullException(nameof(elementSubject)) : elementSubject.owningMembership?.membershipOwningNamespace; } /// @@ -161,10 +155,42 @@ internal static INamespace ComputeOwningNamespace(this IElement elementSubject) /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static string ComputeQualifiedName(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (elementSubject == null) + { + throw new ArgumentNullException(nameof(elementSubject)); + } + + if (elementSubject.owningNamespace == null) + { + return null; + } + + if (elementSubject.name != null) + { + var membersWithTheName = elementSubject.owningNamespace.member.Where(x => x.name == elementSubject.name).ToList(); + + if (membersWithTheName.IndexOf(elementSubject) != 0) + { + return null; + } + } + + if (elementSubject.owningNamespace.owner == null) + { + return elementSubject.EscapedName(); + } + + var parentQualifiedName = elementSubject.owningNamespace.qualifiedName; + var currentEscaped = elementSubject.EscapedName(); + + if (string.IsNullOrWhiteSpace(parentQualifiedName) || string.IsNullOrWhiteSpace(currentEscaped)) + { + return null; + } + + return $"{parentQualifiedName}::{currentEscaped}"; } /// @@ -179,7 +205,7 @@ internal static string ComputeQualifiedName(this IElement elementSubject) [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static string ComputeShortName(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return elementSubject == null ? throw new ArgumentNullException(nameof(elementSubject)) : elementSubject.EffectiveShortName(); } /// @@ -191,12 +217,11 @@ internal static string ComputeShortName(this IElement elementSubject) /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeTextualRepresentation(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return elementSubject == null ? throw new ArgumentNullException(nameof(elementSubject)) : [..elementSubject.ownedElement.OfType()]; } - + /// /// Return name, if that is not null, otherwise the shortName, if that is not null, otherwise null. If /// the returned value is non-null, it is returned as-is if it has the form of a basic name, or, @@ -209,10 +234,26 @@ internal static List ComputeTextualRepresentation(this I /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static string ComputeEscapedNameOperation(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (elementSubject == null) + { + throw new ArgumentNullException(nameof(elementSubject)); + } + + var targetName = elementSubject.name; + + if (string.IsNullOrWhiteSpace(targetName)) + { + targetName = elementSubject.shortName; + } + + if (string.IsNullOrWhiteSpace(targetName)) + { + return null; + } + + return targetName.QueryIsBasicName() ? targetName : targetName.ToUnrestrictedName(); } /// @@ -225,10 +266,9 @@ internal static string ComputeEscapedNameOperation(this IElement elementSubject) /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static string ComputeEffectiveShortNameOperation(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return elementSubject == null ? throw new ArgumentNullException(nameof(elementSubject)) : elementSubject.DeclaredShortName; } /// @@ -240,10 +280,9 @@ internal static string ComputeEffectiveShortNameOperation(this IElement elementS /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static string ComputeEffectiveNameOperation(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return elementSubject == null ? throw new ArgumentNullException(nameof(elementSubject)) : elementSubject.DeclaredName; } /// @@ -255,10 +294,9 @@ internal static string ComputeEffectiveNameOperation(this IElement elementSubjec /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static INamespace ComputeLibraryNamespaceOperation(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return elementSubject == null ? throw new ArgumentNullException(nameof(elementSubject)) : elementSubject.OwningRelationship?.LibraryNamespace(); } /// @@ -276,10 +314,29 @@ internal static INamespace ComputeLibraryNamespaceOperation(this IElement elemen /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static string ComputePathOperation(this IElement elementSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (elementSubject == null) + { + throw new ArgumentNullException(nameof(elementSubject)); + } + + var qualifiedName = elementSubject.qualifiedName; + + if (!string.IsNullOrWhiteSpace(qualifiedName)) + { + return qualifiedName; + } + + if (elementSubject.OwningRelationship == null) + { + return string.Empty; + } + + var ownedRelatedElementsIndex = elementSubject.OwningRelationship.OwnedRelatedElement.ToList().IndexOf(elementSubject) +1; + var parentPath = elementSubject.OwningRelationship.Path(); + + return $"{parentPath}/{ownedRelatedElementsIndex}"; } } } diff --git a/SysML2.NET/Extend/MembershipExtensions.cs b/SysML2.NET/Extend/MembershipExtensions.cs index a565e8a3..c5eec4c3 100644 --- a/SysML2.NET/Extend/MembershipExtensions.cs +++ b/SysML2.NET/Extend/MembershipExtensions.cs @@ -60,7 +60,7 @@ internal static string ComputeMemberElementId(this IMembership membershipSubject [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static INamespace ComputeMembershipOwningNamespace(this IMembership membershipSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return membershipSubject == null ? throw new ArgumentNullException() : membershipSubject.OwningRelatedElement as INamespace; } /// diff --git a/SysML2.NET/Extend/NamespaceExtensions.cs b/SysML2.NET/Extend/NamespaceExtensions.cs index adbcb160..c890c1a8 100644 --- a/SysML2.NET/Extend/NamespaceExtensions.cs +++ b/SysML2.NET/Extend/NamespaceExtensions.cs @@ -21,7 +21,9 @@ namespace SysML2.NET.Core.POCO.Root.Namespaces { using System; + using System.Collections.Frozen; using System.Collections.Generic; + using System.Linq; using SysML2.NET.Core.Root.Namespaces; using SysML2.NET.Core.POCO.Root.Annotations; @@ -45,7 +47,7 @@ internal static class NamespaceExtensions [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeImportedMembership(this INamespace namespaceSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return namespaceSubject == null ? throw new ArgumentNullException(nameof(namespaceSubject)) : namespaceSubject.ImportedMemberships([]); } /// @@ -60,7 +62,7 @@ internal static List ComputeImportedMembership(this INamespace name [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeMember(this INamespace namespaceSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return namespaceSubject == null ? throw new ArgumentNullException(nameof(namespaceSubject)) : [..namespaceSubject.membership.Select(x => x.MemberElement)]; } /// @@ -75,7 +77,7 @@ internal static List ComputeMember(this INamespace namespaceSubject) [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeMembership(this INamespace namespaceSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return namespaceSubject == null ? throw new ArgumentNullException(nameof(namespaceSubject)) : [..namespaceSubject.ownedMembership.Union(namespaceSubject.importedMembership)]; } /// @@ -87,10 +89,9 @@ internal static List ComputeMembership(this INamespace namespaceSub /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeOwnedImport(this INamespace namespaceSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return namespaceSubject == null ? throw new ArgumentNullException(nameof(namespaceSubject)) : [..namespaceSubject.OwnedRelationship.OfType()]; } /// @@ -120,7 +121,7 @@ internal static List ComputeOwnedMember(this INamespace namespaceSubje [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeOwnedMembership(this INamespace namespaceSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return namespaceSubject == null ? throw new ArgumentNullException(nameof(namespaceSubject)) : [..namespaceSubject.OwnedRelationship.OfType()]; } /// @@ -203,10 +204,30 @@ internal static List ComputeVisibleMembershipsOperation(this INames /// /// The expected collection of /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeImportedMembershipsOperation(this INamespace namespaceSubject, List excluded) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + var importedMemberships = namespaceSubject.ownedImport.Where(x => !excluded.Contains(x.importOwningNamespace)) + .SelectMany(x => x.ImportedMemberships(excluded)) + .Distinct() + .ToList(); + + var ownedMembershipNames = namespaceSubject.ownedMembership + .Select(m => m.MemberName) + .Where(name => name != null) + .ToHashSet(StringComparer.Ordinal); + + var importedMembershipsNameFrequency = importedMemberships + .GroupBy(x => x.MemberName, StringComparer.Ordinal) + .ToFrozenDictionary(g => g.Key, g => g.Count(), StringComparer.Ordinal); + + var nonCollidingImportedMemberships = importedMemberships.Where(x => + { + var name = x.MemberName; + + return !string.IsNullOrWhiteSpace(name) && !ownedMembershipNames.Contains(name) && (!importedMembershipsNameFrequency.TryGetValue(name, out var frequency) || frequency <= 1); + }).ToList(); + + return nonCollidingImportedMemberships; } /// diff --git a/SysML2.NET/Extend/OwningMembershipExtensions.cs b/SysML2.NET/Extend/OwningMembershipExtensions.cs index e9adbc72..fdd513d0 100644 --- a/SysML2.NET/Extend/OwningMembershipExtensions.cs +++ b/SysML2.NET/Extend/OwningMembershipExtensions.cs @@ -22,6 +22,7 @@ namespace SysML2.NET.Core.POCO.Root.Namespaces { using System; using System.Collections.Generic; + using System.Linq; using SysML2.NET.Core.Root.Namespaces; using SysML2.NET.Core.POCO.Root.Annotations; @@ -42,10 +43,9 @@ internal static class OwningMembershipExtensions /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IElement ComputeOwnedMemberElement(this IOwningMembership owningMembershipSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return owningMembershipSubject == null ? throw new ArgumentNullException(nameof(owningMembershipSubject)) : owningMembershipSubject.OwnedRelatedElement.Single(); } /// @@ -72,10 +72,9 @@ internal static string ComputeOwnedMemberElementId(this IOwningMembership owning /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static string ComputeOwnedMemberName(this IOwningMembership owningMembershipSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return owningMembershipSubject == null ? throw new ArgumentNullException(nameof(owningMembershipSubject)) : owningMembershipSubject.ownedMemberElement.name; } /// diff --git a/SysML2.NET/Extensions/StringExtensions.cs b/SysML2.NET/Extensions/StringExtensions.cs new file mode 100644 index 00000000..6e543f05 --- /dev/null +++ b/SysML2.NET/Extensions/StringExtensions.cs @@ -0,0 +1,80 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// Copyright 2022-2026 Starion Group S.A. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +// ------------------------------------------------------------------------------------------------ + +namespace SysML2.NET.Extensions +{ + using System.Text; + using System.Text.RegularExpressions; + + using SysML2.NET.LexicalRules; + + /// + /// Provides extensions methods for the class + /// + public static class StringExtensions + { + /// + /// Verifies that a name can be qualified as basic name. cfr. Section 7.2.2 of the SysML 2 Specification for the full definition + /// + /// The name to verifies + /// true if the name is a basic name, false otherwise + public static bool QueryIsBasicName(this string name) + { + return Regex.IsMatch(name, "^[a-zA-Z_][a-zA-Z0-9_]*$") && !Keywords.ReservedKeywords.Contains(name); + } + + /// + /// Converts a non-basic name to an unrestricted name. cfr. Section 8.2.2.3 of KerML specification + /// + /// A non-basic name + /// The unrestricted name + public static string ToUnrestrictedName(this string name) + { + if (string.IsNullOrEmpty(name)) + { + return "''"; + } + + var stringBuilder = new StringBuilder(); + + stringBuilder.Append("'"); + + foreach (var character in name) + { + switch (character) + { + case '\'': stringBuilder.Append(@"\'"); break; + case '\"': stringBuilder.Append(@"\"""); break; + case '\b': stringBuilder.Append(@"\b"); break; + case '\f': stringBuilder.Append(@"\f"); break; + case '\t': stringBuilder.Append(@"\t"); break; + case '\n': stringBuilder.Append(@"\n"); break; + case '\\': stringBuilder.Append(@"\\"); break; + default: + stringBuilder.Append(character); + break; + } + } + + stringBuilder.Append("'"); + return stringBuilder.ToString(); + } + } +} diff --git a/SysML2.NET/Extensions/SymbolicKeywordKindExtensions.cs b/SysML2.NET/Extensions/SymbolicKeywordKindExtensions.cs new file mode 100644 index 00000000..484c093d --- /dev/null +++ b/SysML2.NET/Extensions/SymbolicKeywordKindExtensions.cs @@ -0,0 +1,72 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// Copyright 2022-2026 Starion Group S.A. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +// ------------------------------------------------------------------------------------------------ + +namespace SysML2.NET.Extensions +{ + using System; + + using SysML2.NET.LexicalRules; + + /// + /// Provides extensions methods for the enumeration + /// + public static class SymbolicKeywordKindExtensions + { + /// + /// Gets the associated keyword of a + /// + /// The instance + /// The associated keyword + /// If the provided is not recognized + public static string GetKeyword(this SymbolicKeywordKind symbolicKeywordKind) + { + return symbolicKeywordKind switch + { + SymbolicKeywordKind.DefinedBy => "defined by", + SymbolicKeywordKind.Specializes => "specializes", + SymbolicKeywordKind.Subsets => "subsets", + SymbolicKeywordKind.References => "references", + SymbolicKeywordKind.Crosses => "crosses", + SymbolicKeywordKind.Redefines => "redefines", + _ => throw new ArgumentOutOfRangeException(nameof(symbolicKeywordKind)) + }; + } + + /// + /// Gets the associated symbol of a + /// + /// The instance + /// The associated symbol + /// If the provided is not recognized + public static string GetSymbol(this SymbolicKeywordKind symbolicKeywordKind) + { + return symbolicKeywordKind switch + { + SymbolicKeywordKind.DefinedBy => ":", + SymbolicKeywordKind.Specializes => ":>", + SymbolicKeywordKind.Subsets => ":>", + SymbolicKeywordKind.References => "::>", + SymbolicKeywordKind.Crosses => "=>", + SymbolicKeywordKind.Redefines => ":>>", + _ => throw new ArgumentOutOfRangeException(nameof(symbolicKeywordKind)) + }; + } + } +} diff --git a/SysML2.NET/LexicalRules/Keywords.cs b/SysML2.NET/LexicalRules/Keywords.cs new file mode 100644 index 00000000..61285e82 --- /dev/null +++ b/SysML2.NET/LexicalRules/Keywords.cs @@ -0,0 +1,47 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// Copyright 2022-2026 Starion Group S.A. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +// ------------------------------------------------------------------------------------------------ + +namespace SysML2.NET.LexicalRules +{ + using System.Collections.Frozen; + using System.Collections.Generic; + + /// + /// Provides access to specification defined keywords + /// + public static class Keywords + { + /// + /// Provides all specification defined reserved keywords + /// + public static readonly FrozenSet ReservedKeywords = new List + { + "about", "abstract", "accept", "action", "actor", "after", "alias", "all", "allocate", "allocation", "analysis", + "and", "as", "assert", "assign", "assume", "at", "attribute", "bind", "binding", "by", "calc", "case", "comment", "concern", "connect", "connection", "constant", + "constraint", "crosses", "decide", "def", "default", "defined", "dependency", "derived", "do", "doc", "else", "end", "entry", "enum", "event", "exhibit", "exit", + "expose", "false", "filter", "first", "flow", "for", "fork", "frame", "from", "hastype", "if", "implies", "import", "in", "include", "individual", "inout", + "interface", "istype", "item", "join", "language", "library", "locale", "loop", "merge", "message", "meta", "metadata", "nonunique", "not", "null", "objective", + "occurrence", "of", "or", "ordered", "out", "package", "parallel", "part", "perform", "port", "private", "protected", "public", "redefines", "ref", "references", + "render", "rendering", "rep", "require", "requirement", "return", "satisfy", "send", "snapshot", "specializes", "stakeholder", "standard", "state", "subject", + "subsets", "succession", "terminate", "then", "timeslice", "to", "transition", "true", "until", "use", "variant", "variation", "verification", "verify", "via", + "view", "viewpoint", "when", "while", "xor" + }.ToFrozenSet(); + } +} diff --git a/SysML2.NET/LexicalRules/SymbolicKeywordKind.cs b/SysML2.NET/LexicalRules/SymbolicKeywordKind.cs new file mode 100644 index 00000000..f3128d32 --- /dev/null +++ b/SysML2.NET/LexicalRules/SymbolicKeywordKind.cs @@ -0,0 +1,58 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// Copyright 2022-2026 Starion Group S.A. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// +// ------------------------------------------------------------------------------------------------ + +namespace SysML2.NET.LexicalRules +{ + /// + /// Defines keyword that could also have symbolic representation + /// + public enum SymbolicKeywordKind + { + /// + /// Declares the DEFINED_BY case + /// + DefinedBy, + + /// + /// Declares the SPECIALIZES case + /// + Specializes, + + /// + /// Declares the SUBSETS case + /// + Subsets, + + /// + /// Declares the REFERENCES case + /// + References, + + /// + /// Declares the CROSSES case + /// + Crosses, + + /// + /// Declares the REDEFINES case + /// + Redefines + } +} diff --git a/SysML2.NET/SysML2.NET.csproj b/SysML2.NET/SysML2.NET.csproj index 1de788c4..6f03c9a4 100644 --- a/SysML2.NET/SysML2.NET.csproj +++ b/SysML2.NET/SysML2.NET.csproj @@ -27,6 +27,7 @@ +