From 9e5fea5c886470d9ac9baa20b01f28405c616d57 Mon Sep 17 00:00:00 2001 From: atheate Date: Wed, 29 Jul 2026 14:07:01 +0200 Subject: [PATCH] Fix #134 + Expression family extension --- ...structorExpressionExtensionsTestFixture.cs | 47 +++++ ...eferenceExpressionExtensionsTestFixture.cs | 192 +++++++++++++++++- ...vocationExpressionExtensionsTestFixture.cs | 58 ++++++ .../LiteralExpressionExtensionsTestFixture.cs | 69 +++++++ .../NullExpressionExtensionsTestFixture.cs | 63 ++++++ .../Extend/ConstructorExpressionExtensions.cs | 41 ++-- .../FeatureReferenceExpressionExtensions.cs | 161 +++++++++++---- .../Extend/InvocationExpressionExtensions.cs | 46 ++--- .../Extend/LiteralExpressionExtensions.cs | 49 ++--- SysML2.NET/Extend/NullExpressionExtensions.cs | 49 ++--- 10 files changed, 633 insertions(+), 142 deletions(-) create mode 100644 SysML2.NET.Tests/Extend/ConstructorExpressionExtensionsTestFixture.cs create mode 100644 SysML2.NET.Tests/Extend/InvocationExpressionExtensionsTestFixture.cs create mode 100644 SysML2.NET.Tests/Extend/LiteralExpressionExtensionsTestFixture.cs create mode 100644 SysML2.NET.Tests/Extend/NullExpressionExtensionsTestFixture.cs diff --git a/SysML2.NET.Tests/Extend/ConstructorExpressionExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ConstructorExpressionExtensionsTestFixture.cs new file mode 100644 index 00000000..dcf61555 --- /dev/null +++ b/SysML2.NET.Tests/Extend/ConstructorExpressionExtensionsTestFixture.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.Tests.Extend +{ + using System; + + using NUnit.Framework; + + using SysML2.NET.Core.POCO.Kernel.Expressions; + + [TestFixture] + public class ConstructorExpressionExtensionsTestFixture + { + [Test] + public void VerifyComputeRedefinedModelLevelEvaluableOperation() + { + Assert.That( + () => ((IConstructorExpression)null).ComputeRedefinedModelLevelEvaluableOperation([]), + Throws.TypeOf()); + + var subject = new ConstructorExpression(); + + // For later: populated case depends on ComputeArgument, still a stub (out of batch). + Assert.That( + () => subject.ComputeRedefinedModelLevelEvaluableOperation([]), + Throws.TypeOf()); + } + } +} diff --git a/SysML2.NET.Tests/Extend/FeatureReferenceExpressionExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/FeatureReferenceExpressionExtensionsTestFixture.cs index 1ec91c35..7e05af71 100644 --- a/SysML2.NET.Tests/Extend/FeatureReferenceExpressionExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/FeatureReferenceExpressionExtensionsTestFixture.cs @@ -1,38 +1,210 @@ -// ------------------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------------------------------- // -// +// // 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.Tests.Extend { using System; - + using NUnit.Framework; - + + using SysML2.NET.Core.POCO.Core.Features; + using SysML2.NET.Core.POCO.Core.Types; + using SysML2.NET.Core.POCO.Kernel.Behaviors; using SysML2.NET.Core.POCO.Kernel.Expressions; + using SysML2.NET.Core.POCO.Kernel.FeatureValues; + using SysML2.NET.Core.POCO.Kernel.Metadata; + using SysML2.NET.Core.POCO.Root.Annotations; + using SysML2.NET.Core.POCO.Root.Namespaces; + using SysML2.NET.Extensions; + + using Type = SysML2.NET.Core.POCO.Core.Types.Type; [TestFixture] public class FeatureReferenceExpressionExtensionsTestFixture { [Test] - public void ComputeReferent_ThrowsNotSupportedException() + public void VerifyComputeReferent() + { + // Branch 1: null subject → ArgumentNullException. + Assert.That( + () => ((IFeatureReferenceExpression)null).ComputeReferent(), + Throws.TypeOf()); + + // Branch 2: no ownedMembership at all → null. + var subject = new FeatureReferenceExpression(); + + Assert.That(subject.ComputeReferent(), Is.Null); + + // Branch 3: only a ParameterMembership → rejected by the reject(ParameterMembership) filter → null. + var parameterFeature = new Feature(); + var parameterMembership = new ParameterMembership(); + subject.AssignOwnership(parameterMembership, parameterFeature); + + Assert.That(subject.ComputeReferent(), Is.Null); + + // Branch 4: first non-parameter membership whose MemberElement is NOT a Feature → null. + var nonFeatureSubject = new FeatureReferenceExpression(); + var nonFeatureMembership = new Membership(); + nonFeatureSubject.AssignOwnership(nonFeatureMembership); + nonFeatureMembership.MemberElement = new Namespace(); + + Assert.That(nonFeatureSubject.ComputeReferent(), Is.Null); + + // Branch 5: first non-parameter membership whose MemberElement IS a Feature → returns that Feature. + var referentFeature = new Feature(); + var featureMembership = new Membership(); + var positiveSubject = new FeatureReferenceExpression(); + positiveSubject.AssignOwnership(featureMembership); + featureMembership.MemberElement = referentFeature; + + Assert.That(positiveSubject.ComputeReferent(), Is.SameAs(referentFeature)); + + // Branch 6: ParameterMembership first, then a plain Membership with a Feature → the + // ParameterMembership is rejected; the Membership is the first non-parameter one → its Feature. + var mixedSubject = new FeatureReferenceExpression(); + var mixedParamFeature = new Feature(); + var mixedParamMembership = new ParameterMembership(); + mixedSubject.AssignOwnership(mixedParamMembership, mixedParamFeature); + + var mixedReferentFeature = new Feature(); + var mixedMembership = new Membership(); + mixedSubject.AssignOwnership(mixedMembership); + mixedMembership.MemberElement = mixedReferentFeature; + + Assert.That(mixedSubject.ComputeReferent(), Is.SameAs(mixedReferentFeature)); + } + + [Test] + public void VerifyComputeRedefinedModelLevelEvaluableOperation() + { + // Branch 1: null subject → ArgumentNullException. + Assert.That( + () => ((IFeatureReferenceExpression)null).ComputeRedefinedModelLevelEvaluableOperation([]), + Throws.TypeOf()); + + // Branch 2: referent == null (only a ParameterMembership, which ComputeReferent rejects) → true. + var noReferentSubject = new FeatureReferenceExpression(); + var parameterFeature = new Feature(); + var parameterMembership = new ParameterMembership(); + noReferentSubject.AssignOwnership(parameterMembership, parameterFeature); + + // Branch 3: referent is a plain Feature with no featuringType and no FeatureValue → falls through + // to "value expression is null → true". + var plainReferent = new Feature(); + var plainMembership = new Membership(); + var plainSubject = new FeatureReferenceExpression(); + plainSubject.AssignOwnership(plainMembership); + plainMembership.MemberElement = plainReferent; + + // Branch 4: visited already contains the referent → cycle guard → false. + var visitedReferent = new Feature(); + var visitedMembership = new Membership(); + var visitedSubject = new FeatureReferenceExpression(); + visitedSubject.AssignOwnership(visitedMembership); + visitedMembership.MemberElement = visitedReferent; + + // Branch 5: referent.owningType is a Metaclass → true. + var metaclassReferent = new Feature(); + var metaclass = new Metaclass(); + metaclass.AssignOwnership(new FeatureMembership(), metaclassReferent); + var metaclassRefMembership = new Membership(); + var metaclassSubject = new FeatureReferenceExpression(); + metaclassSubject.AssignOwnership(metaclassRefMembership); + metaclassRefMembership.MemberElement = metaclassReferent; + + using (Assert.EnterMultipleScope()) + { + Assert.That(noReferentSubject.ComputeRedefinedModelLevelEvaluableOperation([]), Is.True); + Assert.That(plainSubject.ComputeRedefinedModelLevelEvaluableOperation([]), Is.True); + Assert.That(visitedSubject.ComputeRedefinedModelLevelEvaluableOperation([visitedReferent]), Is.False); + Assert.That(metaclassSubject.ComputeRedefinedModelLevelEvaluableOperation([]), Is.True); + } + } + + [Test] + public void VerifyComputeRedefinedEvaluateOperation() { - Assert.That(() => ((IFeatureReferenceExpression)null).ComputeReferent(), Throws.TypeOf()); + // Branch 1: null subject → ArgumentNullException. + Assert.That( + () => ((IFeatureReferenceExpression)null).ComputeRedefinedEvaluateOperation(null), + Throws.TypeOf()); + + // Branch 2: target is null (not a Type) → empty sequence. + var subject = new FeatureReferenceExpression(); + + Assert.That(subject.ComputeRedefinedEvaluateOperation(null), Is.Empty); + + // Branch 3: target is a non-Type element (a Comment) → empty sequence. + Assert.That(subject.ComputeRedefinedEvaluateOperation(new Comment()), Is.Empty); + + // Branch 4: target IS a Type, but none of its features redefine the referent, and the + // referent has an empty featuringType → falls to else → returns [referent]. + var referent = new Feature(); + var referentMembership = new Membership(); + var referentSubject = new FeatureReferenceExpression(); + referentSubject.AssignOwnership(referentMembership); + referentMembership.MemberElement = referent; + + var emptyType = new Type(); + var referentResult = referentSubject.ComputeRedefinedEvaluateOperation(emptyType); + + // Branch 5: target IS a Type, no feature redefines the referent, and the referent HAS a + // non-empty featuringType (via a TypeFeaturing) → falls to else → empty sequence. + var featuredReferent = new Feature(); + var featuringType = new Type(); + featuredReferent.AssignOwnership(new TypeFeaturing { FeatureOfType = featuredReferent, FeaturingType = featuringType }); + + var featuredMembership = new Membership(); + var featuredSubject = new FeatureReferenceExpression(); + featuredSubject.AssignOwnership(featuredMembership); + featuredMembership.MemberElement = featuredReferent; + + // Branch 6 (recursion): target IS a Type whose feature redefines the referent and carries a + // FeatureValue whose value is a LiteralExpression. FeatureReferenceExpression.Evaluate + // recurses into the value Expression's Evaluate; LiteralExpression.Evaluate IS + // implemented (returns Sequence{self}), so this asserts the real evaluated result. + var recursionReferent = new Feature(); + var recursionMembership = new Membership(); + var recursionSubject = new FeatureReferenceExpression(); + recursionSubject.AssignOwnership(recursionMembership); + recursionMembership.MemberElement = recursionReferent; + + var redefiningFeature = new Feature(); + redefiningFeature.AssignOwnership(new Redefinition { RedefinedFeature = recursionReferent }); + + var literalValue = new LiteralBoolean(); + redefiningFeature.AssignOwnership(new FeatureValue(), literalValue); + + var targetType = new Type(); + targetType.AssignOwnership(new FeatureMembership(), redefiningFeature); + + var recursionResult = recursionSubject.ComputeRedefinedEvaluateOperation(targetType); + + using (Assert.EnterMultipleScope()) + { + Assert.That(referentResult, Has.Count.EqualTo(1)); + Assert.That(referentResult[0], Is.SameAs(referent)); + Assert.That(featuredSubject.ComputeRedefinedEvaluateOperation(new Type()), Is.Empty); + Assert.That(recursionResult, Has.Count.EqualTo(1)); + Assert.That(recursionResult[0], Is.SameAs(literalValue)); + } } } } diff --git a/SysML2.NET.Tests/Extend/InvocationExpressionExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/InvocationExpressionExtensionsTestFixture.cs new file mode 100644 index 00000000..b4f38ff5 --- /dev/null +++ b/SysML2.NET.Tests/Extend/InvocationExpressionExtensionsTestFixture.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.Tests.Extend +{ + using System; + + using NUnit.Framework; + + using SysML2.NET.Core.POCO.Kernel.Expressions; + + [TestFixture] + public class InvocationExpressionExtensionsTestFixture + { + [Test] + public void VerifyComputeRedefinedModelLevelEvaluableOperation() + { + Assert.That( + () => ((IInvocationExpression)null).ComputeRedefinedModelLevelEvaluableOperation([]), + Throws.TypeOf()); + + var subject = new InvocationExpression(); + + // For later: populated case depends on ComputeArgument / Function.ComputeIsModelLevelEvaluable, still a stub (out of batch). + Assert.That( + () => subject.ComputeRedefinedModelLevelEvaluableOperation([]), + Throws.TypeOf()); + } + + [Test] + public void VerifyComputeRedefinedEvaluateOperation() + { + var subject = new InvocationExpression(); + + // For later: deferred — needs Function-application engine (no OCL). + Assert.That( + () => subject.ComputeRedefinedEvaluateOperation(null), + Throws.TypeOf()); + } + } +} diff --git a/SysML2.NET.Tests/Extend/LiteralExpressionExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/LiteralExpressionExtensionsTestFixture.cs new file mode 100644 index 00000000..6bfe194e --- /dev/null +++ b/SysML2.NET.Tests/Extend/LiteralExpressionExtensionsTestFixture.cs @@ -0,0 +1,69 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// 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.Tests.Extend +{ + using System; + + using NUnit.Framework; + + using SysML2.NET.Core.POCO.Kernel.Expressions; + + [TestFixture] + public class LiteralExpressionExtensionsTestFixture + { + [Test] + public void VerifyComputeRedefinedModelLevelEvaluableOperation() + { + Assert.That( + () => ((ILiteralExpression)null).ComputeRedefinedModelLevelEvaluableOperation([]), + Throws.TypeOf()); + + var subject = new LiteralExpression(); + + using (Assert.EnterMultipleScope()) + { + Assert.That(subject.ComputeRedefinedModelLevelEvaluableOperation([]), Is.True); + + // visited is unused by the OCL body (`true`); a null visited must still return true. + Assert.That(subject.ComputeRedefinedModelLevelEvaluableOperation(null), Is.True); + } + } + + [Test] + public void VerifyComputeRedefinedEvaluateOperation() + { + Assert.That( + () => ((ILiteralExpression)null).ComputeRedefinedEvaluateOperation(null), + Throws.TypeOf()); + + var subject = new LiteralExpression(); + + // target is unused by the OCL body (`Sequence{self}`); pass null. + var result = subject.ComputeRedefinedEvaluateOperation(null); + + using (Assert.EnterMultipleScope()) + { + Assert.That(result, Has.Count.EqualTo(1)); + Assert.That(result[0], Is.SameAs(subject)); + } + } + } +} diff --git a/SysML2.NET.Tests/Extend/NullExpressionExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/NullExpressionExtensionsTestFixture.cs new file mode 100644 index 00000000..c7b56918 --- /dev/null +++ b/SysML2.NET.Tests/Extend/NullExpressionExtensionsTestFixture.cs @@ -0,0 +1,63 @@ +// ------------------------------------------------------------------------------------------------- +// +// +// 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.Tests.Extend +{ + using System; + + using NUnit.Framework; + + using SysML2.NET.Core.POCO.Kernel.Expressions; + + [TestFixture] + public class NullExpressionExtensionsTestFixture + { + [Test] + public void VerifyComputeRedefinedModelLevelEvaluableOperation() + { + Assert.That( + () => ((INullExpression)null).ComputeRedefinedModelLevelEvaluableOperation([]), + Throws.TypeOf()); + + var subject = new NullExpression(); + + using (Assert.EnterMultipleScope()) + { + Assert.That(subject.ComputeRedefinedModelLevelEvaluableOperation([]), Is.True); + + // visited is unused by the OCL body (`true`); a null visited must still return true. + Assert.That(subject.ComputeRedefinedModelLevelEvaluableOperation(null), Is.True); + } + } + + [Test] + public void VerifyComputeRedefinedEvaluateOperation() + { + Assert.That( + () => ((INullExpression)null).ComputeRedefinedEvaluateOperation(null), + Throws.TypeOf()); + + var subject = new NullExpression(); + + // target is unused by the OCL body (`Sequence{}`); pass null. + Assert.That(subject.ComputeRedefinedEvaluateOperation(null), Is.Empty); + } + } +} diff --git a/SysML2.NET/Extend/ConstructorExpressionExtensions.cs b/SysML2.NET/Extend/ConstructorExpressionExtensions.cs index 2fd066ba..74ef8a3f 100644 --- a/SysML2.NET/Extend/ConstructorExpressionExtensions.cs +++ b/SysML2.NET/Extend/ConstructorExpressionExtensions.cs @@ -1,20 +1,20 @@ // ------------------------------------------------------------------------------------------------- // -// -// Copyright (C) 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 -// +// +// Copyright (C) 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. -// +// // // ------------------------------------------------------------------------------------------------ @@ -22,20 +22,13 @@ namespace SysML2.NET.Core.POCO.Kernel.Expressions { using System; using System.Collections.Generic; + using System.Linq; - using SysML2.NET.Core.Core.Types; - using SysML2.NET.Core.Root.Namespaces; using SysML2.NET.Core.POCO.Core.Features; - using SysML2.NET.Core.POCO.Core.Types; - using SysML2.NET.Core.POCO.Kernel.Behaviors; - using SysML2.NET.Core.POCO.Kernel.Functions; - using SysML2.NET.Core.POCO.Root.Annotations; - using SysML2.NET.Core.POCO.Root.Elements; - using SysML2.NET.Core.POCO.Root.Namespaces; /// - /// The class provides extensions methods for - /// the interface + /// The class provides extensions methods for + /// the interface /// internal static class ConstructorExpressionExtensions { @@ -50,7 +43,7 @@ internal static class ConstructorExpressionExtensions /// /// /// - /// The subject + /// The subject /// /// /// No documentation provided @@ -58,10 +51,14 @@ internal static class ConstructorExpressionExtensions /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static bool ComputeRedefinedModelLevelEvaluableOperation(this IConstructorExpression constructorExpressionSubject, List visited) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (constructorExpressionSubject == null) + { + throw new ArgumentNullException(nameof(constructorExpressionSubject)); + } + + return constructorExpressionSubject.argument.All(argument => argument.ModelLevelEvaluable(visited)); } } } diff --git a/SysML2.NET/Extend/FeatureReferenceExpressionExtensions.cs b/SysML2.NET/Extend/FeatureReferenceExpressionExtensions.cs index 16b4a8eb..b62b3558 100644 --- a/SysML2.NET/Extend/FeatureReferenceExpressionExtensions.cs +++ b/SysML2.NET/Extend/FeatureReferenceExpressionExtensions.cs @@ -1,20 +1,20 @@ // ------------------------------------------------------------------------------------------------- // -// -// Copyright (C) 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 -// +// +// Copyright (C) 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. -// +// // // ------------------------------------------------------------------------------------------------ @@ -22,20 +22,19 @@ namespace SysML2.NET.Core.POCO.Kernel.Expressions { using System; using System.Collections.Generic; + using System.Linq; - using SysML2.NET.Core.Core.Types; - using SysML2.NET.Core.Root.Namespaces; using SysML2.NET.Core.POCO.Core.Features; using SysML2.NET.Core.POCO.Core.Types; using SysML2.NET.Core.POCO.Kernel.Behaviors; + using SysML2.NET.Core.POCO.Kernel.FeatureValues; using SysML2.NET.Core.POCO.Kernel.Functions; - using SysML2.NET.Core.POCO.Root.Annotations; + using SysML2.NET.Core.POCO.Kernel.Metadata; using SysML2.NET.Core.POCO.Root.Elements; - using SysML2.NET.Core.POCO.Root.Namespaces; /// - /// The class provides extensions methods for - /// the interface + /// The class provides extensions methods for + /// the interface /// internal static class FeatureReferenceExpressionExtensions { @@ -56,24 +55,35 @@ internal static class FeatureReferenceExpressionExtensions /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IFeature ComputeReferent(this IFeatureReferenceExpression featureReferenceExpressionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (featureReferenceExpressionSubject == null) + { + throw new ArgumentNullException(nameof(featureReferenceExpressionSubject)); + } + + var nonParameterMembership = featureReferenceExpressionSubject.ownedMembership + .FirstOrDefault(membership => membership is not IParameterMembership); + + return nonParameterMembership?.MemberElement as IFeature; } /// - /// A FeatureReferenceExpression is model-level evaluable if it's referent - ///
  • conforms to the self-reference feature Anything::self;
  • - ///
  • is an Expression that is model-level evaluable;
  • - ///
  • has an owningType that is a Metaclass or MetadataFeature; or
  • - ///
  • has no featuringTypes and, if it has a FeatureValue, the value Expression is model-level - /// evaluable.
+ /// A FeatureReferenceExpression is model-level evaluable if it's referent + ///
    + ///
  • conforms to the self-reference feature Anything::self;
  • + ///
  • is an Expression that is model-level evaluable;
  • + ///
  • has an owningType that is a Metaclass or MetadataFeature; or
  • + ///
  • + /// has no featuringTypes and, if it has a FeatureValue, the value Expression is model-level + /// evaluable. + ///
  • + ///
///
/// /// OCL2.0: @@ -91,7 +101,7 @@ internal static IFeature ComputeReferent(this IFeatureReferenceExpression featur /// /// /// - /// The subject + /// The subject /// /// /// No documentation provided @@ -99,22 +109,69 @@ internal static IFeature ComputeReferent(this IFeatureReferenceExpression featur /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static bool ComputeRedefinedModelLevelEvaluableOperation(this IFeatureReferenceExpression featureReferenceExpressionSubject, List visited) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (featureReferenceExpressionSubject == null) + { + throw new ArgumentNullException(nameof(featureReferenceExpressionSubject)); + } + + var referent = featureReferenceExpressionSubject.referent; + + if (referent == null || referent.SpecializesFromLibrary("Anything::self")) + { + return true; + } + + if (visited.Contains(referent)) + { + return false; + } + + var next = new List(visited) { referent }; + + if (referent is IExpression referentExpression && referentExpression.ModelLevelEvaluable(next)) + { + return true; + } + + if (referent.owningType is IMetaclass or IMetadataFeature) + { + return true; + } + + if (referent.featuringType.Count != 0) + { + return false; + } + + var valueExpression = referent.ownedMembership.OfType().FirstOrDefault()?.value; + + return valueExpression == null || valueExpression.ModelLevelEvaluable(next); } /// - /// First, determine a value Expression for the referent:
    - ///
  • If the target Element is a Type that has a feature that is the referent or (directly - /// or indirectly) redefines it, then the value Expression of the FeatureValue for that feature (if - /// any).
  • Else, if the referent has no featuringTypes, the value - /// Expression of the FeatureValue for the referent (if any).
- /// Then:
  • If such a - /// value Expression exists, return the result of evaluating that Expression on the target.
  • - ///
  • Else, if the referent is not an Expression, return the referent.
  • - ///
  • Else return the empty sequence.
+ /// First, determine a value Expression for the referent: + ///
    + ///
  • + /// If the target Element is a Type that has a feature that is the referent or (directly + /// or indirectly) redefines it, then the value Expression of the FeatureValue for that feature (if + /// any). + ///
  • + ///
  • + /// Else, if the referent has no featuringTypes, the value + /// Expression of the FeatureValue for the referent (if any). + ///
  • + ///
+ /// Then: + ///
    + ///
  • + /// If such a + /// value Expression exists, return the result of evaluating that Expression on the target. + ///
  • + ///
  • Else, if the referent is not an Expression, return the referent.
  • + ///
  • Else return the empty sequence.
  • + ///
///
/// /// OCL2.0: @@ -135,7 +192,7 @@ internal static bool ComputeRedefinedModelLevelEvaluableOperation(this IFeatureR /// /// /// - /// The subject + /// The subject /// /// /// No documentation provided @@ -143,10 +200,36 @@ internal static bool ComputeRedefinedModelLevelEvaluableOperation(this IFeatureR /// /// The expected collection of /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeRedefinedEvaluateOperation(this IFeatureReferenceExpression featureReferenceExpressionSubject, IElement target) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (featureReferenceExpressionSubject == null) + { + throw new ArgumentNullException(nameof(featureReferenceExpressionSubject)); + } + + if (target is not IType targetType) + { + return []; + } + + var referent = featureReferenceExpressionSubject.referent; + + var redefiningFeatures = targetType.feature + .Where(feature => feature.ownedRedefinition.Any(redefinition => redefinition.RedefinedFeature == referent)) + .ToList(); + + if (redefiningFeatures.Count != 0) + { + return + [ + .. redefiningFeatures + .Select(feature => feature.ownedMembership.OfType().FirstOrDefault()?.value) + .Where(valueExpression => valueExpression != null) + .SelectMany(valueExpression => valueExpression.Evaluate(target)) + ]; + } + + return referent != null && referent.featuringType.Count == 0 ? [referent] : []; } } } diff --git a/SysML2.NET/Extend/InvocationExpressionExtensions.cs b/SysML2.NET/Extend/InvocationExpressionExtensions.cs index b004aad5..ebcdd8e3 100644 --- a/SysML2.NET/Extend/InvocationExpressionExtensions.cs +++ b/SysML2.NET/Extend/InvocationExpressionExtensions.cs @@ -1,20 +1,20 @@ // ------------------------------------------------------------------------------------------------- // -// -// Copyright (C) 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 -// +// +// Copyright (C) 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. -// +// // // ------------------------------------------------------------------------------------------------ @@ -22,20 +22,15 @@ namespace SysML2.NET.Core.POCO.Kernel.Expressions { using System; using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; - using SysML2.NET.Core.Core.Types; - using SysML2.NET.Core.Root.Namespaces; using SysML2.NET.Core.POCO.Core.Features; - using SysML2.NET.Core.POCO.Core.Types; - using SysML2.NET.Core.POCO.Kernel.Behaviors; - using SysML2.NET.Core.POCO.Kernel.Functions; - using SysML2.NET.Core.POCO.Root.Annotations; using SysML2.NET.Core.POCO.Root.Elements; - using SysML2.NET.Core.POCO.Root.Namespaces; /// - /// The class provides extensions methods for - /// the interface + /// The class provides extensions methods for + /// the interface /// internal static class InvocationExpressionExtensions { @@ -51,7 +46,7 @@ internal static class InvocationExpressionExtensions /// /// /// - /// The subject + /// The subject /// /// /// No documentation provided @@ -59,10 +54,15 @@ internal static class InvocationExpressionExtensions /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static bool ComputeRedefinedModelLevelEvaluableOperation(this IInvocationExpression invocationExpressionSubject, List visited) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (invocationExpressionSubject == null) + { + throw new ArgumentNullException(nameof(invocationExpressionSubject)); + } + + return invocationExpressionSubject.argument.All(argument => argument.ModelLevelEvaluable(visited)) + && invocationExpressionSubject.function.isModelLevelEvaluable; } /// @@ -71,7 +71,7 @@ internal static bool ComputeRedefinedModelLevelEvaluableOperation(this IInvocati /// possible, then return an empty sequence. /// /// - /// The subject + /// The subject /// /// /// No documentation provided @@ -79,7 +79,7 @@ internal static bool ComputeRedefinedModelLevelEvaluableOperation(this IInvocati /// /// The expected collection of /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [ExcludeFromCodeCoverage] internal static List ComputeRedefinedEvaluateOperation(this IInvocationExpression invocationExpressionSubject, IElement target) { throw new NotSupportedException("Create a GitHub issue when this method is required"); diff --git a/SysML2.NET/Extend/LiteralExpressionExtensions.cs b/SysML2.NET/Extend/LiteralExpressionExtensions.cs index d3924913..efdfcf49 100644 --- a/SysML2.NET/Extend/LiteralExpressionExtensions.cs +++ b/SysML2.NET/Extend/LiteralExpressionExtensions.cs @@ -1,20 +1,20 @@ // ------------------------------------------------------------------------------------------------- // -// -// Copyright (C) 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 -// +// +// Copyright (C) 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. -// +// // // ------------------------------------------------------------------------------------------------ @@ -23,19 +23,12 @@ namespace SysML2.NET.Core.POCO.Kernel.Expressions using System; using System.Collections.Generic; - using SysML2.NET.Core.Core.Types; - using SysML2.NET.Core.Root.Namespaces; using SysML2.NET.Core.POCO.Core.Features; - using SysML2.NET.Core.POCO.Core.Types; - using SysML2.NET.Core.POCO.Kernel.Behaviors; - using SysML2.NET.Core.POCO.Kernel.Functions; - using SysML2.NET.Core.POCO.Root.Annotations; using SysML2.NET.Core.POCO.Root.Elements; - using SysML2.NET.Core.POCO.Root.Namespaces; /// - /// The class provides extensions methods for - /// the interface + /// The class provides extensions methods for + /// the interface /// internal static class LiteralExpressionExtensions { @@ -49,7 +42,7 @@ internal static class LiteralExpressionExtensions /// /// /// - /// The subject + /// The subject /// /// /// No documentation provided @@ -57,10 +50,14 @@ internal static class LiteralExpressionExtensions /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static bool ComputeRedefinedModelLevelEvaluableOperation(this ILiteralExpression literalExpressionSubject, List visited) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (literalExpressionSubject == null) + { + throw new ArgumentNullException(nameof(literalExpressionSubject)); + } + + return true; } /// @@ -73,7 +70,7 @@ internal static bool ComputeRedefinedModelLevelEvaluableOperation(this ILiteralE /// /// /// - /// The subject + /// The subject /// /// /// No documentation provided @@ -81,10 +78,14 @@ internal static bool ComputeRedefinedModelLevelEvaluableOperation(this ILiteralE /// /// The expected collection of /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeRedefinedEvaluateOperation(this ILiteralExpression literalExpressionSubject, IElement target) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (literalExpressionSubject == null) + { + throw new ArgumentNullException(nameof(literalExpressionSubject)); + } + + return [literalExpressionSubject]; } } } diff --git a/SysML2.NET/Extend/NullExpressionExtensions.cs b/SysML2.NET/Extend/NullExpressionExtensions.cs index bbfde5e9..e2f8ea47 100644 --- a/SysML2.NET/Extend/NullExpressionExtensions.cs +++ b/SysML2.NET/Extend/NullExpressionExtensions.cs @@ -1,20 +1,20 @@ // ------------------------------------------------------------------------------------------------- // -// -// Copyright (C) 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 -// +// +// Copyright (C) 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. -// +// // // ------------------------------------------------------------------------------------------------ @@ -23,19 +23,12 @@ namespace SysML2.NET.Core.POCO.Kernel.Expressions using System; using System.Collections.Generic; - using SysML2.NET.Core.Core.Types; - using SysML2.NET.Core.Root.Namespaces; using SysML2.NET.Core.POCO.Core.Features; - using SysML2.NET.Core.POCO.Core.Types; - using SysML2.NET.Core.POCO.Kernel.Behaviors; - using SysML2.NET.Core.POCO.Kernel.Functions; - using SysML2.NET.Core.POCO.Root.Annotations; using SysML2.NET.Core.POCO.Root.Elements; - using SysML2.NET.Core.POCO.Root.Namespaces; /// - /// The class provides extensions methods for - /// the interface + /// The class provides extensions methods for + /// the interface /// internal static class NullExpressionExtensions { @@ -49,7 +42,7 @@ internal static class NullExpressionExtensions /// /// /// - /// The subject + /// The subject /// /// /// No documentation provided @@ -57,10 +50,14 @@ internal static class NullExpressionExtensions /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static bool ComputeRedefinedModelLevelEvaluableOperation(this INullExpression nullExpressionSubject, List visited) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (nullExpressionSubject == null) + { + throw new ArgumentNullException(nameof(nullExpressionSubject)); + } + + return true; } /// @@ -73,7 +70,7 @@ internal static bool ComputeRedefinedModelLevelEvaluableOperation(this INullExpr /// /// /// - /// The subject + /// The subject /// /// /// No documentation provided @@ -81,10 +78,14 @@ internal static bool ComputeRedefinedModelLevelEvaluableOperation(this INullExpr /// /// The expected collection of /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeRedefinedEvaluateOperation(this INullExpression nullExpressionSubject, IElement target) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (nullExpressionSubject == null) + { + throw new ArgumentNullException(nameof(nullExpressionSubject)); + } + + return []; } } }