diff --git a/SysML2.NET.Tests/Extend/ConnectorExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ConnectorExtensionsTestFixture.cs
index 16538453..a254f8ba 100644
--- a/SysML2.NET.Tests/Extend/ConnectorExtensionsTestFixture.cs
+++ b/SysML2.NET.Tests/Extend/ConnectorExtensionsTestFixture.cs
@@ -1,68 +1,310 @@
-// -------------------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------------------
//
-//
+//
// 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.Associations;
using SysML2.NET.Core.POCO.Kernel.Connectors;
+ using SysML2.NET.Extensions;
+
+ using PocoType = SysML2.NET.Core.POCO.Core.Types.Type;
[TestFixture]
public class ConnectorExtensionsTestFixture
{
[Test]
- public void ComputeAssociation_ThrowsNotSupportedException()
+ public void VerifyComputeAssociation()
{
- Assert.That(() => ((IConnector)null).ComputeAssociation(), Throws.TypeOf());
+ Assert.That(() => ((IConnector)null).ComputeAssociation(), Throws.TypeOf());
+
+ var connector = new Connector();
+
+ // Empty: no OwnedRelationship → returns empty list.
+ Assert.That(connector.ComputeAssociation(), Is.Empty);
+
+ // Negative: FeatureTyping pointing at a non-Association Type → excluded.
+ var nonAssociationType = new PocoType();
+ var typingToNonAssociation = new FeatureTyping { Type = nonAssociationType };
+ connector.AssignOwnership(typingToNonAssociation);
+
+ Assert.That(connector.ComputeAssociation(), Is.Empty);
+
+ // Positive: FeatureTyping pointing at an Association → returned.
+ var association1 = new Association();
+ var typingToAssociation1 = new FeatureTyping { Type = association1 };
+ connector.AssignOwnership(typingToAssociation1);
+
+ Assert.That(connector.ComputeAssociation(), Is.EqualTo(new[] { association1 }));
+
+ // Populated: second Association also returned, in iteration order.
+ var association2 = new Association();
+ var typingToAssociation2 = new FeatureTyping { Type = association2 };
+ connector.AssignOwnership(typingToAssociation2);
+
+ Assert.That(connector.ComputeAssociation(), Is.EqualTo(new[] { association1, association2 }));
}
-
+
[Test]
- public void ComputeConnectorEnd_ThrowsNotSupportedException()
+ public void VerifyComputeConnectorEnd()
{
- Assert.That(() => ((IConnector)null).ComputeConnectorEnd(), Throws.TypeOf());
+ Assert.That(() => ((IConnector)null).ComputeConnectorEnd(), Throws.TypeOf());
+
+ var connector = new Connector();
+
+ // Empty: no features → empty list.
+ Assert.That(connector.ComputeConnectorEnd(), Is.Empty);
+
+ // Negative: feature with IsEnd = false → excluded.
+ var nonEndFeature = new Feature { IsEnd = false };
+ var nonEndMembership = new FeatureMembership();
+ connector.AssignOwnership(nonEndMembership, nonEndFeature);
+
+ Assert.That(connector.ComputeConnectorEnd(), Is.Empty);
+
+ // Positive: feature with IsEnd = true → returned.
+ var endFeature1 = new Feature { IsEnd = true };
+ var endMembership1 = new FeatureMembership();
+ connector.AssignOwnership(endMembership1, endFeature1);
+
+ Assert.That(connector.ComputeConnectorEnd(), Is.EqualTo(new[] { endFeature1 }));
+
+ // Discrimination: second IsEnd=true also returned, IsEnd=false remains excluded.
+ var endFeature2 = new Feature { IsEnd = true };
+ var endMembership2 = new FeatureMembership();
+ connector.AssignOwnership(endMembership2, endFeature2);
+
+ Assert.That(connector.ComputeConnectorEnd(), Is.EqualTo(new[] { endFeature1, endFeature2 }));
}
-
+
[Test]
- public void ComputeDefaultFeaturingType_ThrowsNotSupportedException()
+ public void VerifyComputeDefaultFeaturingType()
{
- Assert.That(() => ((IConnector)null).ComputeDefaultFeaturingType(), Throws.TypeOf());
+ Assert.That(() => ((IConnector)null).ComputeDefaultFeaturingType(), Throws.TypeOf());
+
+ var connector = new Connector();
+
+ // Empty: no relatedFeatures (no connectorEnds) → null.
+ Assert.That(connector.ComputeDefaultFeaturingType(), Is.Null);
+
+ // Set up helper: creates a connector end Feature with a ReferenceSubsetting pointing at
+ // a relatedTarget Feature, and adds the end to the connector via FeatureMembership.
+ // Returns the relatedTarget Feature so callers can wire featuringType on it.
+ static Feature AddConnectorEnd(Connector owningConnector, Feature relatedTarget)
+ {
+ var endFeature = new Feature { IsEnd = true };
+ var referenceSubsetting = new ReferenceSubsetting { ReferencedFeature = relatedTarget };
+ endFeature.AssignOwnership(referenceSubsetting);
+
+ var membership = new FeatureMembership();
+ owningConnector.AssignOwnership(membership, endFeature);
+
+ return endFeature;
+ }
+
+ // Single relatedFeature with no featuringType → closure = {relatedTarget itself};
+ // relatedTarget.IsFeaturedWithin(relatedTarget): featuringType is empty → All() is
+ // vacuously true → relatedTarget qualifies as a common featuring type.
+ // nearestCommonFeaturingTypes = {relatedTarget} (nothing dominates it) → returns relatedTarget.
+ var relatedTarget1 = new Feature();
+ AddConnectorEnd(connector, relatedTarget1);
+
+ Assert.That(connector.ComputeDefaultFeaturingType(), Is.SameAs(relatedTarget1));
+
+ // Two relatedFeatures sharing exactly one common featuringType T:
+ // Build a fresh connector to avoid state from the previous case.
+ var connector2 = new Connector();
+ var commonType = new PocoType();
+
+ var sharedTarget1 = new Feature();
+ var typeFeaturing1 = new TypeFeaturing { FeatureOfType = sharedTarget1, FeaturingType = commonType };
+ sharedTarget1.AssignOwnership(typeFeaturing1);
+
+ var sharedTarget2 = new Feature();
+ var typeFeaturing2 = new TypeFeaturing { FeatureOfType = sharedTarget2, FeaturingType = commonType };
+ sharedTarget2.AssignOwnership(typeFeaturing2);
+
+ AddConnectorEnd(connector2, sharedTarget1);
+ AddConnectorEnd(connector2, sharedTarget2);
+
+ Assert.That(connector2.ComputeDefaultFeaturingType(), Is.SameAs(commonType));
+
+ // Disjoint featuringTypes → no common type → null.
+ var connector3 = new Connector();
+ var typeForFirst = new PocoType();
+ var typeForSecond = new PocoType();
+
+ var disjointTarget1 = new Feature();
+ var typeFeaturingDisjoint1 = new TypeFeaturing { FeatureOfType = disjointTarget1, FeaturingType = typeForFirst };
+ disjointTarget1.AssignOwnership(typeFeaturingDisjoint1);
+
+ var disjointTarget2 = new Feature();
+ var typeFeaturingDisjoint2 = new TypeFeaturing { FeatureOfType = disjointTarget2, FeaturingType = typeForSecond };
+ disjointTarget2.AssignOwnership(typeFeaturingDisjoint2);
+
+ AddConnectorEnd(connector3, disjointTarget1);
+ AddConnectorEnd(connector3, disjointTarget2);
+
+ Assert.That(connector3.ComputeDefaultFeaturingType(), Is.Null);
+
+ // Cycle: featureA.featuringType = [featureB], featureB.featuringType = [featureA].
+ // The closure must terminate (cycle-safe BFS); result is deterministic.
+ var connector4 = new Connector();
+ var cycleFeatureA = new Feature();
+ var cycleFeatureB = new Feature();
+
+ var typeFeaturingAtB = new TypeFeaturing { FeatureOfType = cycleFeatureA, FeaturingType = cycleFeatureB };
+ cycleFeatureA.AssignOwnership(typeFeaturingAtB);
+
+ var typeFeaturingBtoA = new TypeFeaturing { FeatureOfType = cycleFeatureB, FeaturingType = cycleFeatureA };
+ cycleFeatureB.AssignOwnership(typeFeaturingBtoA);
+
+ AddConnectorEnd(connector4, cycleFeatureA);
+ AddConnectorEnd(connector4, cycleFeatureB);
+
+ // Must not throw or infinite-loop; result may be null or one of the features depending
+ // on the IsFeaturedWithin evaluation — we just verify it terminates and returns a value.
+ Assert.That(() => connector4.ComputeDefaultFeaturingType(), Throws.Nothing);
}
-
+
[Test]
- public void ComputeRelatedFeature_ThrowsNotSupportedException()
+ public void VerifyComputeRelatedFeature()
{
- Assert.That(() => ((IConnector)null).ComputeRelatedFeature(), Throws.TypeOf());
+ Assert.That(() => ((IConnector)null).ComputeRelatedFeature(), Throws.TypeOf());
+
+ var connector = new Connector();
+
+ // Empty: no connectorEnds → empty list.
+ Assert.That(connector.ComputeRelatedFeature(), Is.Empty);
+
+ // ConnectorEnd with null ownedReferenceSubsetting → filtered out → empty.
+ var endWithNoSubsetting = new Feature { IsEnd = true };
+ var noSubsettingMembership = new FeatureMembership();
+ connector.AssignOwnership(noSubsettingMembership, endWithNoSubsetting);
+
+ Assert.That(connector.ComputeRelatedFeature(), Is.Empty);
+
+ // ConnectorEnd with a ReferenceSubsetting whose SubsettedFeature is set → that feature returned.
+ var targetFeature1 = new Feature();
+ var referenceSubsetting1 = new ReferenceSubsetting { ReferencedFeature = targetFeature1 };
+ var endWithSubsetting1 = new Feature { IsEnd = true };
+ endWithSubsetting1.AssignOwnership(referenceSubsetting1);
+
+ var subsettingMembership1 = new FeatureMembership();
+ connector.AssignOwnership(subsettingMembership1, endWithSubsetting1);
+
+ Assert.That(connector.ComputeRelatedFeature(), Is.EqualTo(new[] { targetFeature1 }));
+
+ // Second end with a distinct SubsettedFeature → both returned in connector-end order.
+ var targetFeature2 = new Feature();
+ var referenceSubsetting2 = new ReferenceSubsetting { ReferencedFeature = targetFeature2 };
+ var endWithSubsetting2 = new Feature { IsEnd = true };
+ endWithSubsetting2.AssignOwnership(referenceSubsetting2);
+
+ var subsettingMembership2 = new FeatureMembership();
+ connector.AssignOwnership(subsettingMembership2, endWithSubsetting2);
+
+ Assert.That(connector.ComputeRelatedFeature(), Is.EqualTo(new[] { targetFeature1, targetFeature2 }));
}
-
+
[Test]
- public void ComputeSourceFeature_ThrowsNotSupportedException()
+ public void VerifyComputeSourceFeature()
{
- Assert.That(() => ((IConnector)null).ComputeSourceFeature(), Throws.TypeOf());
+ Assert.That(() => ((IConnector)null).ComputeSourceFeature(), Throws.TypeOf());
+
+ var connector = new Connector();
+
+ // Empty: no relatedFeatures → null.
+ Assert.That(connector.ComputeSourceFeature(), Is.Null);
+
+ // Wire one connectorEnd with a ReferencedFeature so relatedFeature is populated.
+ var targetFeature1 = new Feature();
+ var referenceSubsetting1 = new ReferenceSubsetting { ReferencedFeature = targetFeature1 };
+ var endFeature1 = new Feature { IsEnd = true };
+ endFeature1.AssignOwnership(referenceSubsetting1);
+
+ var membership1 = new FeatureMembership();
+ connector.AssignOwnership(membership1, endFeature1);
+
+ // Single relatedFeature → that feature is the source.
+ Assert.That(connector.ComputeSourceFeature(), Is.SameAs(targetFeature1));
+
+ // Two relatedFeatures → first is returned (not the second).
+ var targetFeature2 = new Feature();
+ var referenceSubsetting2 = new ReferenceSubsetting { ReferencedFeature = targetFeature2 };
+ var endFeature2 = new Feature { IsEnd = true };
+ endFeature2.AssignOwnership(referenceSubsetting2);
+
+ var membership2 = new FeatureMembership();
+ connector.AssignOwnership(membership2, endFeature2);
+
+ Assert.That(connector.ComputeSourceFeature(), Is.SameAs(targetFeature1));
}
-
+
[Test]
- public void ComputeTargetFeature_ThrowsNotSupportedException()
+ public void VerifyComputeTargetFeature()
{
- Assert.That(() => ((IConnector)null).ComputeTargetFeature(), Throws.TypeOf());
+ Assert.That(() => ((IConnector)null).ComputeTargetFeature(), Throws.TypeOf());
+
+ var connector = new Connector();
+
+ // Zero relatedFeatures → empty list (not null).
+ Assert.That(connector.ComputeTargetFeature(), Is.Empty);
+
+ // One relatedFeature → still empty (size < 2).
+ var targetFeature1 = new Feature();
+ var referenceSubsetting1 = new ReferenceSubsetting { ReferencedFeature = targetFeature1 };
+ var endFeature1 = new Feature { IsEnd = true };
+ endFeature1.AssignOwnership(referenceSubsetting1);
+
+ var membership1 = new FeatureMembership();
+ connector.AssignOwnership(membership1, endFeature1);
+
+ Assert.That(connector.ComputeTargetFeature(), Is.Empty);
+
+ // Two relatedFeatures [target1, target2] → targetFeature = [target2].
+ var targetFeature2 = new Feature();
+ var referenceSubsetting2 = new ReferenceSubsetting { ReferencedFeature = targetFeature2 };
+ var endFeature2 = new Feature { IsEnd = true };
+ endFeature2.AssignOwnership(referenceSubsetting2);
+
+ var membership2 = new FeatureMembership();
+ connector.AssignOwnership(membership2, endFeature2);
+
+ Assert.That(connector.ComputeTargetFeature(), Is.EqualTo(new[] { targetFeature2 }));
+
+ // Three relatedFeatures [target1, target2, target3] → targetFeature = [target2, target3].
+ var targetFeature3 = new Feature();
+ var referenceSubsetting3 = new ReferenceSubsetting { ReferencedFeature = targetFeature3 };
+ var endFeature3 = new Feature { IsEnd = true };
+ endFeature3.AssignOwnership(referenceSubsetting3);
+
+ var membership3 = new FeatureMembership();
+ connector.AssignOwnership(membership3, endFeature3);
+
+ Assert.That(connector.ComputeTargetFeature(), Is.EqualTo(new[] { targetFeature2, targetFeature3 }));
}
}
}
diff --git a/SysML2.NET.Tests/Extend/TransitionUsageExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/TransitionUsageExtensionsTestFixture.cs
index 8b0e1050..c8789e81 100644
--- a/SysML2.NET.Tests/Extend/TransitionUsageExtensionsTestFixture.cs
+++ b/SysML2.NET.Tests/Extend/TransitionUsageExtensionsTestFixture.cs
@@ -35,6 +35,8 @@ namespace SysML2.NET.Tests.Extend
using SysML2.NET.Core.Systems.States;
using SysML2.NET.Extensions;
+ using PocoFeature = SysML2.NET.Core.POCO.Core.Features.Feature;
+
[TestFixture]
public class TransitionUsageExtensionsTestFixture
{
@@ -149,13 +151,80 @@ public void VerifyComputeTarget()
// No Succession in ownedMember → succession is null → null.
Assert.That(transitionUsage.ComputeTarget(), Is.Null);
- // Succession present but targetFeature access hits ConnectorExtensions.ComputeTargetFeature stub.
- // For Later: populated path depends on ConnectorExtensions.ComputeTargetFeature
- // at SysML2.NET/Extend/ConnectorExtensions.cs:171, which is still a stub.
- var succession = new Succession();
- transitionUsage.AssignOwnership(new OwningMembership(), succession);
+ // Helper: wire a connector end onto the given succession.
+ // Creates a Feature(IsEnd=true) with a ReferenceSubsetting pointing at relatedTarget,
+ // wrapped in a FeatureMembership owned by the succession.
+ static void AddSuccessionEnd(Succession owningSuccession, IFeature relatedTarget)
+ {
+ var endFeature = new PocoFeature { IsEnd = true };
+ var referenceSubsetting = new ReferenceSubsetting { ReferencedFeature = relatedTarget };
+ endFeature.AssignOwnership(referenceSubsetting);
+
+ var featureMembership = new FeatureMembership();
+ owningSuccession.AssignOwnership(featureMembership, endFeature);
+ }
+
+ // Succession present, one connector end → relatedFeature has 1 element → targetFeature is empty
+ // (GetRange(1, 0)) → firstTargetFeature is null → null.
+ var successionOneEnd = new Succession();
+ transitionUsage.AssignOwnership(new OwningMembership(), successionOneEnd);
+ AddSuccessionEnd(successionOneEnd, new PocoFeature());
+
+ Assert.That(transitionUsage.ComputeTarget(), Is.Null);
+
+ // Succession present, two connector ends, second end's relatedTarget is a plain Feature.
+ // featureTarget of Feature (no chaining) = Feature itself → not IActionUsage → null.
+ var transitionUsage2 = new TransitionUsage();
+ var successionNonAction = new Succession();
+ transitionUsage2.AssignOwnership(new OwningMembership(), successionNonAction);
+
+ var nonActionTarget = new PocoFeature();
+ AddSuccessionEnd(successionNonAction, new PocoFeature());
+ AddSuccessionEnd(successionNonAction, nonActionTarget);
+
+ Assert.That(transitionUsage2.ComputeTarget(), Is.Null);
+
+ // firstTargetFeature.featureTarget == null → null.
+ // Wire the second end's relatedTarget with a FeatureChaining whose ChainingFeature = null,
+ // which makes featureTarget derive to null.
+ var transitionUsage3 = new TransitionUsage();
+ var successionNullFeatureTarget = new Succession();
+ transitionUsage3.AssignOwnership(new OwningMembership(), successionNullFeatureTarget);
+
+ var relatedTargetWithNullChain = new PocoFeature();
+ relatedTargetWithNullChain.AssignOwnership(new FeatureChaining { ChainingFeature = null });
+
+ AddSuccessionEnd(successionNullFeatureTarget, new PocoFeature());
+ AddSuccessionEnd(successionNullFeatureTarget, relatedTargetWithNullChain);
+
+ // relatedTargetWithNullChain.featureTarget = null (last ChainingFeature is null) → null.
+ Assert.That(transitionUsage3.ComputeTarget(), Is.Null);
+
+ // POSITIVE case: two connector ends, the second end's relatedTarget is an ActionUsage.
+ // ActionUsage has no chaining → featureTarget = itself = IActionUsage → returned.
+ var transitionUsage4 = new TransitionUsage();
+ var successionPositive = new Succession();
+ transitionUsage4.AssignOwnership(new OwningMembership(), successionPositive);
+
+ var actionUsageTarget = new ActionUsage();
+ AddSuccessionEnd(successionPositive, new PocoFeature());
+ AddSuccessionEnd(successionPositive, actionUsageTarget);
+
+ Assert.That(transitionUsage4.ComputeTarget(), Is.SameAs(actionUsageTarget));
+
+ // Three connector ends: source, targetEnd2 (ActionUsage), targetEnd3 (plain Feature).
+ // targetFeature = [targetEnd2, targetEnd3]; firstTargetFeature = targetEnd2 (ActionUsage)
+ // → featureTarget = itself → returned.
+ var transitionUsage5 = new TransitionUsage();
+ var successionThreeEnds = new Succession();
+ transitionUsage5.AssignOwnership(new OwningMembership(), successionThreeEnds);
+
+ var actionUsageTarget5 = new ActionUsage();
+ AddSuccessionEnd(successionThreeEnds, new PocoFeature());
+ AddSuccessionEnd(successionThreeEnds, actionUsageTarget5);
+ AddSuccessionEnd(successionThreeEnds, new PocoFeature());
- Assert.That(() => transitionUsage.ComputeTarget(), Throws.TypeOf());
+ Assert.That(transitionUsage5.ComputeTarget(), Is.SameAs(actionUsageTarget5));
}
[Test]
diff --git a/SysML2.NET/Extend/ConnectorExtensions.cs b/SysML2.NET/Extend/ConnectorExtensions.cs
index a84872d2..57b9359e 100644
--- a/SysML2.NET/Extend/ConnectorExtensions.cs
+++ b/SysML2.NET/Extend/ConnectorExtensions.cs
@@ -22,15 +22,11 @@ namespace SysML2.NET.Core.POCO.Kernel.Connectors
{
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.Associations;
- 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
@@ -47,10 +43,14 @@ internal static class ConnectorExtensions
///
/// the computed result
///
- [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static List ComputeAssociation(this IConnector connectorSubject)
{
- throw new NotSupportedException("Create a GitHub issue when this method is required");
+ return connectorSubject == null
+ ? throw new ArgumentNullException(nameof(connectorSubject))
+ : [..connectorSubject.OwnedRelationship
+ .OfType()
+ .Select(featureTyping => featureTyping.Type)
+ .OfType()];
}
///
@@ -62,10 +62,11 @@ internal static List ComputeAssociation(this IConnector connectorS
///
/// the computed result
///
- [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static List ComputeConnectorEnd(this IConnector connectorSubject)
{
- throw new NotSupportedException("Create a GitHub issue when this method is required");
+ return connectorSubject == null
+ ? throw new ArgumentNullException(nameof(connectorSubject))
+ : [..connectorSubject.feature.Where(memberFeature => memberFeature.IsEnd)];
}
///
@@ -94,10 +95,38 @@ internal static List ComputeConnectorEnd(this IConnector connectorSubj
///
/// the computed result
///
- [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static IType ComputeDefaultFeaturingType(this IConnector connectorSubject)
{
- throw new NotSupportedException("Create a GitHub issue when this method is required");
+ if (connectorSubject == null)
+ {
+ throw new ArgumentNullException(nameof(connectorSubject));
+ }
+
+ var relatedFeatures = connectorSubject.relatedFeature;
+
+ if (relatedFeatures.Count == 0)
+ {
+ return null;
+ }
+
+ var fullClosure = ComputeFeaturingTypeClosure(relatedFeatures);
+
+ var commonFeaturingTypes = fullClosure
+ .Where(candidate => relatedFeatures.All(relatedFeature => relatedFeature.IsFeaturedWithin(candidate)))
+ .ToList();
+
+ if (commonFeaturingTypes.Count == 0)
+ {
+ return null;
+ }
+
+ var nearestCommonFeaturingTypes = commonFeaturingTypes
+ .Where(candidateType => !commonFeaturingTypes.Any(successorType =>
+ successorType != candidateType
+ && ComputeFeaturingTypeClosure([successorType]).Contains(candidateType)))
+ .ToList();
+
+ return nearestCommonFeaturingTypes.Count == 0 ? null : nearestCommonFeaturingTypes[0];
}
///
@@ -116,10 +145,14 @@ internal static IType ComputeDefaultFeaturingType(this IConnector connectorSubje
///
/// the computed result
///
- [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static List ComputeRelatedFeature(this IConnector connectorSubject)
{
- throw new NotSupportedException("Create a GitHub issue when this method is required");
+ return connectorSubject == null
+ ? throw new ArgumentNullException(nameof(connectorSubject))
+ : [..connectorSubject.connectorEnd
+ .Select(connectorEndFeature => connectorEndFeature.ownedReferenceSubsetting)
+ .Where(referenceSubsetting => referenceSubsetting != null)
+ .Select(referenceSubsetting => referenceSubsetting.SubsettedFeature)];
}
///
@@ -140,10 +173,16 @@ internal static List ComputeRelatedFeature(this IConnector connectorSu
///
/// the computed result
///
- [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static IFeature ComputeSourceFeature(this IConnector connectorSubject)
{
- throw new NotSupportedException("Create a GitHub issue when this method is required");
+ if (connectorSubject == null)
+ {
+ throw new ArgumentNullException(nameof(connectorSubject));
+ }
+
+ var relatedFeatures = connectorSubject.relatedFeature;
+
+ return relatedFeatures.Count == 0 ? null : relatedFeatures[0];
}
///
@@ -167,11 +206,61 @@ internal static IFeature ComputeSourceFeature(this IConnector connectorSubject)
///
/// the computed result
///
- [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static List ComputeTargetFeature(this IConnector connectorSubject)
{
- throw new NotSupportedException("Create a GitHub issue when this method is required");
+ if (connectorSubject == null)
+ {
+ throw new ArgumentNullException(nameof(connectorSubject));
+ }
+
+ var relatedFeatures = connectorSubject.relatedFeature;
+
+ return relatedFeatures.Count < 2 ? [] : relatedFeatures.GetRange(1, relatedFeatures.Count - 1);
}
+ ///
+ /// Computes the OCL closure(featuringType) over a seed set of nodes.
+ ///
+ ///
+ /// The featuringType navigation is declared on only — pure
+ /// nodes that are not also are sinks. The walk
+ /// is breadth-first and uses a visited-set to guarantee
+ /// termination on cyclic featuringType graphs. The seed elements are included in the
+ /// result, mirroring OCL closure semantics.
+ ///
+ ///
+ /// The seed collection of elements from which to begin the closure.
+ ///
+ ///
+ /// The closure of the seed under the featuringType navigation, in BFS visit order.
+ ///
+ private static List ComputeFeaturingTypeClosure(IEnumerable seed)
+ {
+ var visited = new HashSet();
+ var result = new List();
+ var queue = new Queue(seed);
+
+ while (queue.Count > 0)
+ {
+ var current = queue.Dequeue();
+
+ if (current == null || !visited.Add(current))
+ {
+ continue;
+ }
+
+ result.Add(current);
+
+ if (current is IFeature currentFeature)
+ {
+ foreach (var featuringType in currentFeature.featuringType.Where(featuringType => featuringType != null && !visited.Contains(featuringType)))
+ {
+ queue.Enqueue(featuringType);
+ }
+ }
+ }
+
+ return result;
+ }
}
}