From 8bb9234031bcc377118b5c946a9e1495ecd6a44e Mon Sep 17 00:00:00 2001 From: Arthur Daussy Date: Fri, 31 Jul 2026 12:09:30 +0200 Subject: [PATCH] ST6RI-961 Prevent duplicate implicit Subsettings for structurally equivalent feature chains --- .../org/omg/sysml/adapter/TypeAdapter.java | 26 +++++++++- .../logic/TransitionUsageAdapterTest.java | 51 +++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/TypeAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/TypeAdapter.java index 281526b8a..8a794e940 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/TypeAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/TypeAdapter.java @@ -327,7 +327,31 @@ public boolean isImplicitSpecializationDeclaredFor(EClass eClass) { } public boolean isImplicitSpecializationFor(EClass eClass, Type general) { - return implicitGeneralTypes.getOrDefault(eClass, Collections.emptyList()).contains(general); + return implicitGeneralTypes.getOrDefault(eClass, Collections.emptyList()).stream(). + anyMatch(existingGeneral->areEquivalentImplicitGeneralTypes(existingGeneral, general)); + } + + /** + * Feature chains are derived Types for which object identity is not sufficient: + * two independently created chains may represent the same ordered sequence of + * chaining Features. + */ + protected static boolean areEquivalentImplicitGeneralTypes(Type first, Type second) { + if (first == second) { + return true; + } else if (first instanceof Feature && second instanceof Feature) { + List firstChain = ((Feature)first).getChainingFeature(); + List secondChain = ((Feature)second).getChainingFeature(); + if (!firstChain.isEmpty() && firstChain.size() == secondChain.size()) { + for (int i = 0; i < firstChain.size(); i++) { + if (firstChain.get(i) != secondChain.get(i)) { + return false; + } + } + return true; + } + } + return false; } protected static boolean hasNoConformingSpecializations(Type type, Class kind, Type defaultGeneral) { diff --git a/org.omg.sysml.logic/src/test/java/org/omg/sysml/logic/TransitionUsageAdapterTest.java b/org.omg.sysml.logic/src/test/java/org/omg/sysml/logic/TransitionUsageAdapterTest.java index e281101e6..bfe826cac 100644 --- a/org.omg.sysml.logic/src/test/java/org/omg/sysml/logic/TransitionUsageAdapterTest.java +++ b/org.omg.sysml.logic/src/test/java/org/omg/sysml/logic/TransitionUsageAdapterTest.java @@ -21,15 +21,26 @@ package org.omg.sysml.logic; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +import java.util.List; import org.junit.Test; +import org.omg.sysml.lang.sysml.AcceptActionUsage; import org.omg.sysml.lang.sysml.Feature; import org.omg.sysml.lang.sysml.FeatureDirectionKind; import org.omg.sysml.lang.sysml.ParameterMembership; +import org.omg.sysml.lang.sysml.ReferenceUsage; import org.omg.sysml.lang.sysml.SysMLFactory; import org.omg.sysml.lang.sysml.SysMLPackage; +import org.omg.sysml.lang.sysml.TransitionFeatureKind; +import org.omg.sysml.lang.sysml.TransitionFeatureMembership; import org.omg.sysml.lang.sysml.TransitionUsage; +import org.omg.sysml.lang.sysml.Type; +import org.omg.sysml.util.FeatureUtil; +import org.omg.sysml.util.TypeUtil; /** * Tests transition derived-property computations. @@ -54,4 +65,44 @@ public void transitionSourceDerivationIsReentrantSafeWithoutSuccession() { transition.getOwnedRelationship().add(parameterMembership); assertNull(transition.getSource()); } + + /** + * A structurally equivalent feature chain already registered as an implicit + * Subsetting must not be duplicated when the transition payload default + * general type is computed. + */ + @Test + public void transitionPayloadDoesNotDuplicateEquivalentImplicitSubsetting() { + SysMLLogicStandaloneSetup.doSetup(); + SysMLPackage.eINSTANCE.eClass(); + SysMLFactory factory = SysMLFactory.eINSTANCE; + TransitionUsage transition = factory.createTransitionUsage(); + addParameter(transition, factory.createReferenceUsage()); + ReferenceUsage transitionPayload = factory.createReferenceUsage(); + addParameter(transition, transitionPayload); + + AcceptActionUsage trigger = factory.createAcceptActionUsage(); + ReferenceUsage triggerPayload = factory.createReferenceUsage(); + addParameter(trigger, triggerPayload); + TransitionFeatureMembership triggerMembership = factory.createTransitionFeatureMembership(); + triggerMembership.setKind(TransitionFeatureKind.TRIGGER); + triggerMembership.getOwnedRelatedElement().add(trigger); + transition.getOwnedRelationship().add(triggerMembership); + + Feature existingChain = FeatureUtil.chainFeatures(trigger, triggerPayload); + TypeUtil.addImplicitGeneralTypeTo(transitionPayload, SysMLPackage.Literals.SUBSETTING, existingChain); + + TypeUtil.getImplicitGeneralTypesFor(transitionPayload); + + List implicitSubsettings = TypeUtil.getImplicitGeneralTypesOnly(transitionPayload, SysMLPackage.Literals.SUBSETTING); + assertEquals(1, implicitSubsettings.size()); + assertSame(existingChain, implicitSubsettings.get(0)); + } + + private static void addParameter(Type owner, Feature parameter) { + parameter.setDirection(FeatureDirectionKind.IN); + ParameterMembership membership = SysMLFactory.eINSTANCE.createParameterMembership(); + membership.setOwnedMemberParameter(parameter); + owner.getOwnedRelationship().add(membership); + } }