From c0af352bbb70145b3e19f8a35b8f7aaceb15ce32 Mon Sep 17 00:00:00 2001 From: Arthur Daussy Date: Mon, 20 Jul 2026 13:33:16 +0200 Subject: [PATCH] ST6RI-953 - Extract parsing and linking completion concerns from SysML model adapters Bug: https://openmbee.atlassian.net/browse/ST6RI-953 --- .../omg/sysml/adapter/AnnotationAdapter.java | 15 +- .../org/omg/sysml/adapter/CommentAdapter.java | 6 +- .../omg/sysml/adapter/ConjugationAdapter.java | 31 +- .../omg/sysml/adapter/DefinitionAdapter.java | 6 +- .../omg/sysml/adapter/DependencyAdapter.java | 11 +- .../sysml/adapter/DifferencingAdapter.java | 19 +- .../omg/sysml/adapter/DisjoiningAdapter.java | 38 +- .../org/omg/sysml/adapter/ElementAdapter.java | 18 +- .../adapter/EndFeatureMembershipAdapter.java | 8 +- .../org/omg/sysml/adapter/FeatureAdapter.java | 6 +- .../adapter/FeatureInvertingAdapter.java | 36 +- .../sysml/adapter/FeatureTypingAdapter.java | 26 +- .../org/omg/sysml/adapter/ImportAdapter.java | 11 +- .../sysml/adapter/IntersectingAdapter.java | 19 +- .../sysml/adapter/LiteralStringAdapter.java | 5 +- .../omg/sysml/adapter/MembershipAdapter.java | 9 +- .../sysml/adapter/NamespaceImportAdapter.java | 18 +- .../sysml/adapter/OccurrenceUsageAdapter.java | 7 +- .../adapter/ParameterMembershipAdapter.java | 8 +- .../sysml/adapter/PortConjugationAdapter.java | 21 +- .../omg/sysml/adapter/PortUsageAdapter.java | 9 +- .../sysml/adapter/RedefinitionAdapter.java | 30 +- .../adapter/ReferenceSubsettingAdapter.java | 17 +- .../sysml/adapter/SpecializationAdapter.java | 32 +- .../adapter/SubclassificationAdapter.java | 16 +- .../omg/sysml/adapter/SubsettingAdapter.java | 32 +- .../adapter/TextualRepresentationAdapter.java | 6 +- .../sysml/adapter/TypeFeaturingAdapter.java | 15 +- .../omg/sysml/adapter/UnioningAdapter.java | 19 +- .../org/omg/sysml/adapter/UsageAdapter.java | 11 +- .../omg/sysml/logic/IReferenceSetChecker.java | 32 + .../logic/LexicalNormalizationService.java | 91 +++ .../StructuralModelCompletionService.java | 550 ++++++++++++++++++ .../LexicalNormalizationServiceTest.java | 164 ++++++ .../StructuralModelCompletionServiceTest.java | 487 ++++++++++++++++ 35 files changed, 1398 insertions(+), 431 deletions(-) create mode 100644 org.omg.sysml.logic/src/main/java/org/omg/sysml/logic/IReferenceSetChecker.java create mode 100644 org.omg.sysml.logic/src/main/java/org/omg/sysml/logic/LexicalNormalizationService.java create mode 100644 org.omg.sysml.logic/src/main/java/org/omg/sysml/logic/StructuralModelCompletionService.java create mode 100644 org.omg.sysml.logic/src/test/java/org/omg/sysml/logic/LexicalNormalizationServiceTest.java create mode 100644 org.omg.sysml.logic/src/test/java/org/omg/sysml/logic/StructuralModelCompletionServiceTest.java diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/AnnotationAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/AnnotationAdapter.java index 65addbb32..44eed16bc 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/AnnotationAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/AnnotationAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024, 2025 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,10 +22,7 @@ package org.omg.sysml.adapter; -import org.omg.sysml.lang.sysml.AnnotatingElement; import org.omg.sysml.lang.sysml.Annotation; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.SysMLPackage; public class AnnotationAdapter extends RelationshipAdapter { @@ -39,16 +37,7 @@ public Annotation getTarget() { @Override public void postProcess() { - Annotation obj = getTarget(); - - // If the Annotation is not owned by an AnnotatingElement, then the annotatedElement is the owningRelatedElement. - Object annotatedElement = obj.eGet(SysMLPackage.Literals.ANNOTATION__ANNOTATED_ELEMENT, false); - if (annotatedElement == null) { - Element owningRelatedElement = obj.getOwningRelatedElement(); - if (!(owningRelatedElement instanceof AnnotatingElement)) { - obj.setAnnotatedElement(owningRelatedElement); - } - } + getStructuralModelCompletionService().caseAnnotation(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/CommentAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/CommentAdapter.java index 2d60c5dac..1afbc9abd 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/CommentAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/CommentAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2026 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -19,7 +20,6 @@ package org.omg.sysml.adapter; import org.omg.sysml.lang.sysml.Comment; -import org.omg.sysml.util.ElementUtil; public class CommentAdapter extends AnnotatingElementAdapter { @@ -35,8 +35,6 @@ public Comment getTarget() { @Override public void postProcess() { super.postProcess(); - Comment target = getTarget(); - target.setLocale(ElementUtil.unescapeString(target.getLocale())); - target.setBody(ElementUtil.processCommentBody(target.getBody())); + getLexicalNormalizationService().caseComment(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ConjugationAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ConjugationAdapter.java index 60a890242..8618222f7 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ConjugationAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ConjugationAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,11 +22,7 @@ package org.omg.sysml.adapter; -import org.eclipse.emf.common.util.EList; import org.omg.sysml.lang.sysml.Conjugation; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.SysMLPackage; -import org.omg.sysml.lang.sysml.Type; public class ConjugationAdapter extends RelationshipAdapter { @@ -40,31 +37,7 @@ public Conjugation getTarget() { @Override public void postProcess() { - Conjugation obj = getTarget(); - - // If the conjugatedType is not set, then set it to the owningRelatedElement, if this is a Type, - // otherwise set it to the first ownedRelatedElement. - Object conjugatedType = obj.eGet(SysMLPackage.Literals.CONJUGATION__CONJUGATED_TYPE, false); - if (conjugatedType == null) { - Element owner = obj.getOwningRelatedElement(); - if (owner instanceof Type) { - obj.setConjugatedType((Type)owner); - } else { - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setConjugatedType((Type)ownedRelatedElements.get(0)); - } - } - } - - // If the originalType is not set, set it to the last ownedRelatedElement. - Object originalType = obj.eGet(SysMLPackage.Literals.CONJUGATION__ORIGINAL_TYPE, false); - if (originalType == null) { - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setOriginalType((Type)ownedRelatedElements.get(ownedRelatedElements.size() - 1)); - } - } + getStructuralModelCompletionService().caseConjugation(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DefinitionAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DefinitionAdapter.java index b6f92f314..845192a04 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DefinitionAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DefinitionAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2021, 2024, 2025 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -36,10 +37,7 @@ public Definition getTarget() { @Override public void postProcess() { super.postProcess(); - Definition target = getTarget(); - if (target.isVariation()) { - target.setIsAbstract(true); - } + getStructuralModelCompletionService().caseDefinition(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DependencyAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DependencyAdapter.java index 0cf60033e..39eafba21 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DependencyAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DependencyAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -20,10 +21,7 @@ package org.omg.sysml.adapter; -import org.eclipse.emf.common.util.EList; -import org.eclipse.emf.ecore.util.EObjectResolvingEList; import org.omg.sysml.lang.sysml.Dependency; -import org.omg.sysml.lang.sysml.Element; public class DependencyAdapter extends RelationshipAdapter { @@ -36,12 +34,7 @@ public Dependency getTarget() { } public void postProcess() { - Dependency target = getTarget(); - - // Add all ownedRelatedElements to supplier. - EObjectResolvingEList suppliers = (EObjectResolvingEList)target.getSupplier(); - EList ownedRelatedElements = target.getOwnedRelatedElement(); - ownedRelatedElements.stream().forEachOrdered(suppliers::addUnique); + getStructuralModelCompletionService().caseDependency(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DifferencingAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DifferencingAdapter.java index ba3dbce90..5c7160931 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DifferencingAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DifferencingAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,11 +22,7 @@ package org.omg.sysml.adapter; -import org.eclipse.emf.common.util.EList; import org.omg.sysml.lang.sysml.Differencing; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.Feature; -import org.omg.sysml.lang.sysml.SysMLPackage; public class DifferencingAdapter extends RelationshipAdapter { @@ -40,19 +37,7 @@ public Differencing getTarget() { @Override public void postProcess() { - Differencing obj = getTarget(); - - // If a Differencing is parsed targeting a Feature chain, then the differencingType will be empty, - // but the Differencing will own the differencingType. So, in this case, the differencingType should - // be set to the (last) ownedRelatedelement. - Object differencingType = obj.eGet(SysMLPackage.Literals.DIFFERENCING__DIFFERENCING_TYPE, false); - if (differencingType == null) { - // Handle a differencingType that is a Feature chain. - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setDifferencingType((Feature)ownedRelatedElements.get(ownedRelatedElements.size() - 1)); - } - } + getStructuralModelCompletionService().caseDifferencing(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DisjoiningAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DisjoiningAdapter.java index 6df64089d..130b722cc 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DisjoiningAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/DisjoiningAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,12 +22,7 @@ package org.omg.sysml.adapter; -import org.eclipse.emf.common.util.EList; import org.omg.sysml.lang.sysml.Disjoining; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.Feature; -import org.omg.sysml.lang.sysml.SysMLPackage; -import org.omg.sysml.lang.sysml.Type; public class DisjoiningAdapter extends RelationshipAdapter { @@ -41,37 +37,7 @@ public Disjoining getTarget() { @Override public void postProcess() { - Disjoining obj = getTarget(); - - // If a Disjoining is parsed targeting a Feature chain, then the disjoiningType will be empty, - // but the Disjoining will own the disjoiningType. So, in this case, the disjoiningType should - // be set to the (last) ownedRelatedelement. - Object disjoiningType = obj.eGet(SysMLPackage.Literals.DISJOINING__DISJOINING_TYPE, false); - if (disjoiningType == null) { - // Handle a disjoiningType that is a Feature chain. - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setDisjoiningType((Feature)ownedRelatedElements.get(ownedRelatedElements.size() - 1)); - } - } - - // If the typedDisjoined (source) is empty, set it to the owningRelatedElement, if it is a Type. - // Otherwise, set it to the first ownedRelatedElement, to the first ownedRelatedElement, - // which will be a Feature chain. - Object typeDisjoined = obj.eGet(SysMLPackage.Literals.DISJOINING__TYPE_DISJOINED, false); - if (typeDisjoined == null) { - Element owner = obj.getOwningRelatedElement(); - if (owner instanceof Type) { - // Handle a Disjoining owned by the typeDisjoined. - obj.setTypeDisjoined((Type)owner); - } else { - // Handle a typeDisjoined that is a Feature chain. - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setTypeDisjoined((Feature)ownedRelatedElements.get(0)); - } - } - } + getStructuralModelCompletionService().caseDisjoining(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ElementAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ElementAdapter.java index 454c0371d..fc980bb08 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ElementAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ElementAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2021-2022, 2026 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -30,9 +31,14 @@ import org.omg.sysml.lang.sysml.Namespace; import org.omg.sysml.lang.sysml.SysMLFactory; import org.omg.sysml.lang.sysml.Type; +import org.omg.sysml.logic.LexicalNormalizationService; +import org.omg.sysml.logic.StructuralModelCompletionService; import org.omg.sysml.util.ElementUtil; public class ElementAdapter extends AdapterImpl { + + private final StructuralModelCompletionService structuralModelCompletionService = new StructuralModelCompletionService(); + private final LexicalNormalizationService lexicalNormalizationService = new LexicalNormalizationService(); protected Class kind; protected boolean isTransformed = false; @@ -111,9 +117,15 @@ public MetadataFeature getMetaclassFeature() { // Parse post-processing public void postProcess() { - Element target = getTarget(); - target.setDeclaredName(ElementUtil.unescapeString(target.getDeclaredName())); - target.setDeclaredShortName(ElementUtil.unescapeString(target.getDeclaredShortName())); + getLexicalNormalizationService().caseElement(getTarget()); + } + + protected StructuralModelCompletionService getStructuralModelCompletionService() { + return structuralModelCompletionService; + } + + protected LexicalNormalizationService getLexicalNormalizationService() { + return lexicalNormalizationService; } // Transformation diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/EndFeatureMembershipAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/EndFeatureMembershipAdapter.java index 268494295..ccf245274 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/EndFeatureMembershipAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/EndFeatureMembershipAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2026 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,7 +22,6 @@ package org.omg.sysml.adapter; import org.omg.sysml.lang.sysml.EndFeatureMembership; -import org.omg.sysml.lang.sysml.Feature; public class EndFeatureMembershipAdapter extends FeatureMembershipAdapter { @@ -40,11 +40,7 @@ public EndFeatureMembership getTarget() { @Override public void postProcess() { super.postProcess(); - EndFeatureMembership target = getTarget(); - Feature endFeature = target.getOwnedMemberFeature(); - if (endFeature != null) { - endFeature.setIsEnd(true); - } + getStructuralModelCompletionService().caseEndFeatureMembership(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/FeatureAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/FeatureAdapter.java index d24992bd0..3d3d2edf4 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/FeatureAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/FeatureAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2021-2026 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -85,10 +86,7 @@ public void postProcess() { // Note: Can be individually overridden. protected void setIsVariableIfConstant() { - Feature target = getTarget(); - if (target.isConstant()) { - target.setIsVariable(true); - } + getStructuralModelCompletionService().caseFeature(getTarget()); } // Caching diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/FeatureInvertingAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/FeatureInvertingAdapter.java index 0e0674807..be30c11f7 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/FeatureInvertingAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/FeatureInvertingAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,11 +22,7 @@ package org.omg.sysml.adapter; -import org.eclipse.emf.common.util.EList; import org.omg.sysml.lang.sysml.FeatureInverting; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.Feature; -import org.omg.sysml.lang.sysml.SysMLPackage; public class FeatureInvertingAdapter extends RelationshipAdapter { @@ -40,36 +37,7 @@ public FeatureInverting getTarget() { @Override public void postProcess() { - FeatureInverting obj = getTarget(); - - // If the featureInverted is empty, then set it to the owningRelatedElement of the FeatureInverting, - // if that is a Feature, otherwise set it to the first ownedRelatedElement (which will be a - // Feature chain). - Object featureInverted = obj.eGet(SysMLPackage.Literals.FEATURE_INVERTING__FEATURE_INVERTED, false); - if (featureInverted == null) { - Element owner = obj.getOwningRelatedElement(); - if (owner instanceof Feature) { - // Handle FeatureInverting owned by the featureInverted. - obj.setFeatureInverted((Feature)owner); - } else { - // Handle featureInverted that is a Feature chain. - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setFeatureInverted((Feature)ownedRelatedElements.get(0)); - } - } - } - - // If the invertingFeature is empty, then set it to the last ownedRelatedElement - // (which will be a Feature chain). - Object invertingFeature = obj.eGet(SysMLPackage.Literals.FEATURE_INVERTING__INVERTING_FEATURE, false); - if (invertingFeature == null) { - // Handle invertingFeature that is a Feature chain. - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setInvertingFeature((Feature)ownedRelatedElements.get(ownedRelatedElements.size() - 1)); - } - } + getStructuralModelCompletionService().caseFeatureInverting(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/FeatureTypingAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/FeatureTypingAdapter.java index 441e88661..ddb0eb160 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/FeatureTypingAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/FeatureTypingAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,11 +22,7 @@ package org.omg.sysml.adapter; -import org.eclipse.emf.common.util.EList; import org.omg.sysml.lang.sysml.FeatureTyping; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.Feature; -import org.omg.sysml.lang.sysml.SysMLPackage; public class FeatureTypingAdapter extends SpecializationAdapter { @@ -40,26 +37,7 @@ public FeatureTyping getTarget() { @Override public void postProcess() { - FeatureTyping obj = getTarget(); - - // If the type is empty, then set it to the first owned related element - // (which will be a Feature chain). - Object type = obj.eGet(SysMLPackage.Literals.FEATURE_TYPING__TYPE, false); - if (type == null) { - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setType((Feature)ownedRelatedElements.get(0)); - } - } - - // If the typedFeature is empty, then set it to the owningRelatedElement (if that is a Feature). - Object typedFeature = obj.eGet(SysMLPackage.Literals.FEATURE_TYPING__TYPED_FEATURE, false); - if (typedFeature == null) { - Element owningRelatedElement = obj.getOwningRelatedElement(); - if (owningRelatedElement instanceof Feature) { - obj.setTypedFeature((Feature) owningRelatedElement); - } - } + getStructuralModelCompletionService().caseFeatureTyping(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ImportAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ImportAdapter.java index 4f5aeb141..71f4a1fc1 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ImportAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ImportAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2024, 2025 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -26,8 +27,6 @@ import org.omg.sysml.lang.sysml.Import; import org.omg.sysml.lang.sysml.Membership; import org.omg.sysml.lang.sysml.Namespace; -import org.omg.sysml.lang.sysml.NamespaceImport; -import org.omg.sysml.lang.sysml.VisibilityKind; public abstract class ImportAdapter extends RelationshipAdapter { @@ -42,13 +41,7 @@ public Import getTarget() { @Override public void postProcess() { super.postProcess(); - - // If the target Import is for a filtered import package, set its visibility to PUBLIC. - Import target = getTarget(); - Namespace owningNamespace = target.getImportOwningNamespace(); - if (owningNamespace != null && owningNamespace.getOwningRelationship() instanceof NamespaceImport) { - target.setVisibility(VisibilityKind.PUBLIC); - } + getStructuralModelCompletionService().caseImport(getTarget()); } // Additional operations diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/IntersectingAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/IntersectingAdapter.java index b7e208a6c..6f732cbf6 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/IntersectingAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/IntersectingAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,11 +22,7 @@ package org.omg.sysml.adapter; -import org.eclipse.emf.common.util.EList; import org.omg.sysml.lang.sysml.Intersecting; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.Feature; -import org.omg.sysml.lang.sysml.SysMLPackage; public class IntersectingAdapter extends RelationshipAdapter { @@ -40,19 +37,7 @@ public Intersecting getTarget() { @Override public void postProcess() { - Intersecting obj = getTarget(); - - // If a Intersecting is parsed targeting a Feature chain, then the intersectingType will be empty, - // but the Intersecting will own the intersectingType. So, in this case, the intersectingType should - // be set to the (last) ownedRelatedelement. - Object intersectingType = obj.eGet(SysMLPackage.Literals.INTERSECTING__INTERSECTING_TYPE, false); - if (intersectingType == null) { - // Handle a intersectingType that is a Feature chain. - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setIntersectingType((Feature)ownedRelatedElements.get(ownedRelatedElements.size() - 1)); - } - } + getStructuralModelCompletionService().caseIntersecting(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/LiteralStringAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/LiteralStringAdapter.java index 44f69f58b..657a5a821 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/LiteralStringAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/LiteralStringAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2026 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,7 +22,6 @@ package org.omg.sysml.adapter; import org.omg.sysml.lang.sysml.LiteralString; -import org.omg.sysml.util.ElementUtil; public class LiteralStringAdapter extends ExpressionAdapter { @@ -37,8 +37,7 @@ public LiteralString getTarget() { @Override public void postProcess() { super.postProcess(); - LiteralString target = getTarget(); - target.setValue(ElementUtil.unescapeString(target.getValue())); + getLexicalNormalizationService().caseLiteralString(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/MembershipAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/MembershipAdapter.java index 145f8370c..c0a4cd0d0 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/MembershipAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/MembershipAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2021-2022, 2024 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,8 +22,6 @@ package org.omg.sysml.adapter; import org.omg.sysml.lang.sysml.Membership; -import org.omg.sysml.lang.sysml.OwningMembership; -import org.omg.sysml.util.ElementUtil; public class MembershipAdapter extends RelationshipAdapter { @@ -38,11 +37,7 @@ public Membership getTarget() { @Override public void postProcess() { super.postProcess(); - Membership target = getTarget(); - if (!(target instanceof OwningMembership)) { - target.setMemberName(ElementUtil.unescapeString(target.getMemberName())); - target.setMemberShortName(ElementUtil.unescapeString(target.getMemberShortName())); - } + getLexicalNormalizationService().caseMembership(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/NamespaceImportAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/NamespaceImportAdapter.java index c10f8936a..224030e8e 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/NamespaceImportAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/NamespaceImportAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024, 2025 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -25,10 +26,8 @@ import java.util.Set; import org.eclipse.emf.common.util.EList; -import org.omg.sysml.lang.sysml.Element; import org.omg.sysml.lang.sysml.Membership; import org.omg.sysml.lang.sysml.NamespaceImport; -import org.omg.sysml.lang.sysml.SysMLPackage; import org.omg.sysml.util.NamespaceUtil; import org.omg.sysml.lang.sysml.Namespace; @@ -45,20 +44,7 @@ public NamespaceImport getTarget() { @Override public void postProcess() { super.postProcess(); - - NamespaceImport obj = getTarget(); - // If importedNamespace is empty, then set it to the first ownedRelatedElement, if this is a namespace - // (filling in the implicit import for a filter package). Otherwise, set it to the importOwningNamspace. - Object importedNamespace = obj.eGet(SysMLPackage.Literals.NAMESPACE_IMPORT__IMPORTED_NAMESPACE, false); - if (importedNamespace == null) { - EList ownedRelatedElement = obj.getOwnedRelatedElement(); - if (!ownedRelatedElement.isEmpty() && ownedRelatedElement.get(0) instanceof Namespace) { - // Fill in the implicit import for a filter package. - obj.setImportedNamespace((Namespace)ownedRelatedElement.get(0)); - } else { - obj.setImportedNamespace(obj.getImportOwningNamespace()); - } - } + getStructuralModelCompletionService().caseNamespaceImport(getTarget()); } @Override diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/OccurrenceUsageAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/OccurrenceUsageAdapter.java index 65cd0d56f..2cb284a5c 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/OccurrenceUsageAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/OccurrenceUsageAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2021, 2023-2025 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -39,11 +40,7 @@ public OccurrenceUsage getTarget() { @Override public void postProcess() { super.postProcess(); - - OccurrenceUsage self = getTarget(); - if (self.getPortionKind() != null) { - self.setIsPortion(true); - } + getStructuralModelCompletionService().caseOccurrenceUsage(getTarget()); } // Implicit Generalization diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ParameterMembershipAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ParameterMembershipAdapter.java index 4c4283ef6..fa1059e26 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ParameterMembershipAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ParameterMembershipAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2026 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -20,7 +21,6 @@ package org.omg.sysml.adapter; -import org.omg.sysml.lang.sysml.Feature; import org.omg.sysml.lang.sysml.ParameterMembership; public class ParameterMembershipAdapter extends FeatureMembershipAdapter { @@ -37,11 +37,7 @@ public ParameterMembership getTarget() { @Override public void postProcess() { super.postProcess(); - ParameterMembership target = getTarget(); - Feature parameter = target.getOwnedMemberParameter(); - if (parameter != null) { - parameter.setDirection(target.parameterDirection()); - } + getStructuralModelCompletionService().caseParameterMembership(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/PortConjugationAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/PortConjugationAdapter.java index 80a275eb6..f6e990134 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/PortConjugationAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/PortConjugationAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -22,10 +23,6 @@ package org.omg.sysml.adapter; import org.omg.sysml.lang.sysml.PortConjugation; -import org.omg.sysml.lang.sysml.PortDefinition; -import org.omg.sysml.lang.sysml.ConjugatedPortDefinition; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.SysMLPackage; public class PortConjugationAdapter extends ConjugationAdapter { @@ -41,21 +38,7 @@ public PortConjugation getTarget() { @Override public void postProcess() { super.postProcess(); - - PortConjugation obj = getTarget(); - - // If the originalPortDefinition is empty, then set it to the owner of the conjugatedPortDefinition - // (if that is a PortDefinition). - Object originalPortDefinition = obj.eGet(SysMLPackage.Literals.PORT_CONJUGATION__ORIGINAL_PORT_DEFINITION, false); - if (originalPortDefinition == null) { - ConjugatedPortDefinition conjugatedPortDefinition = obj.getConjugatedPortDefinition(); - if (conjugatedPortDefinition != null) { - Element conjugatedPortDefinitionOwner = conjugatedPortDefinition.getOwner(); - if (conjugatedPortDefinitionOwner instanceof PortDefinition) { - obj.setOriginalPortDefinition((PortDefinition)conjugatedPortDefinitionOwner); - } - } - } + getStructuralModelCompletionService().casePortConjugation(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/PortUsageAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/PortUsageAdapter.java index a52f5e2eb..961b59fd0 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/PortUsageAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/PortUsageAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2021, 2022, 2025, 2026 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -20,13 +21,11 @@ package org.omg.sysml.adapter; -import org.eclipse.emf.ecore.EObject; import org.omg.sysml.lang.sysml.PartDefinition; import org.omg.sysml.lang.sysml.PartUsage; import org.omg.sysml.lang.sysml.PortDefinition; import org.omg.sysml.lang.sysml.PortUsage; import org.omg.sysml.lang.sysml.Type; -import org.omg.sysml.util.UsageUtil; public class PortUsageAdapter extends UsageAdapter { @@ -43,11 +42,7 @@ public PortUsage getTarget() { */ public void postProcess() { super.postProcess(); - PortUsage target = getTarget(); - EObject featuringType = UsageUtil.getExpectedFeaturingTypeOf(target); - if (!(featuringType instanceof PortDefinition || featuringType instanceof PortUsage)) { - target.setIsComposite(false); - } + getStructuralModelCompletionService().casePortUsage(getTarget()); } // Implicit Generalization diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/RedefinitionAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/RedefinitionAdapter.java index 0eeccb4e0..ba514bb7b 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/RedefinitionAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/RedefinitionAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,11 +22,7 @@ package org.omg.sysml.adapter; -import org.eclipse.emf.common.util.EList; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.Feature; import org.omg.sysml.lang.sysml.Redefinition; -import org.omg.sysml.lang.sysml.SysMLPackage; public class RedefinitionAdapter extends SubsettingAdapter { @@ -40,30 +37,7 @@ public Redefinition getTarget() { @Override public void postProcess() { - Redefinition obj = getTarget(); - - // If the redefiningFeature is empty, then set it to the owningRelatedElement, if that is a Feature. - // Otherwise, set it to the first ownedRelatedElement (which will be a Feature chain). - Object redefiningFeature = obj.eGet(SysMLPackage.Literals.REDEFINITION__REDEFINING_FEATURE, false); - if (redefiningFeature == null) { - Element owner = obj.getOwningRelatedElement(); - if (owner instanceof Feature) { - obj.setRedefiningFeature((Feature) owner); - } else { - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setRedefiningFeature((Feature)ownedRelatedElements.get(0)); - } - } - } - - Object redefinedFeature = obj.eGet(SysMLPackage.Literals.REDEFINITION__REDEFINED_FEATURE, false); - if (redefinedFeature == null) { - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setRedefinedFeature((Feature)ownedRelatedElements.get(ownedRelatedElements.size() - 1)); - } - } + getStructuralModelCompletionService().caseRedefinition(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ReferenceSubsettingAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ReferenceSubsettingAdapter.java index 781f86a4e..45c68dbbb 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ReferenceSubsettingAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ReferenceSubsettingAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,11 +22,7 @@ package org.omg.sysml.adapter; -import org.eclipse.emf.common.util.EList; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.Feature; import org.omg.sysml.lang.sysml.ReferenceSubsetting; -import org.omg.sysml.lang.sysml.SysMLPackage; public class ReferenceSubsettingAdapter extends SubsettingAdapter { @@ -40,17 +37,7 @@ public ReferenceSubsetting getTarget() { @Override public void postProcess() { - ReferenceSubsetting obj = getTarget(); - - // If the referencedFeature is empty, then set it to the last ownedRelatedElement - // (which will be a Feature chain). - Object referencedFeature = obj.eGet(SysMLPackage.Literals.REFERENCE_SUBSETTING__REFERENCED_FEATURE, false); - if (referencedFeature == null) { - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setReferencedFeature((Feature)ownedRelatedElements.get(ownedRelatedElements.size() - 1)); - } - } + getStructuralModelCompletionService().caseReferenceSubsetting(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/SpecializationAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/SpecializationAdapter.java index cba4bbde6..1b5d14bef 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/SpecializationAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/SpecializationAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,12 +22,7 @@ package org.omg.sysml.adapter; -import org.eclipse.emf.common.util.EList; import org.omg.sysml.lang.sysml.Specialization; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.Feature; -import org.omg.sysml.lang.sysml.SysMLPackage; -import org.omg.sysml.lang.sysml.Type; public class SpecializationAdapter extends RelationshipAdapter { @@ -40,31 +36,7 @@ public Specialization getTarget() { } public void postProcess() { - Specialization obj = getTarget(); - - // If general is empty, then set it to the last ownedRelatedElement (which will be a Feature chain). - Object general = obj.eGet(SysMLPackage.Literals.SPECIALIZATION__GENERAL, false); - if (general == null) { - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setGeneral((Feature)ownedRelatedElements.get(ownedRelatedElements.size() - 1)); - } - } - - // If specific is empty, then set it to the owningRelatedElement, if that is a Type. - // Otherwise set it to the first ownedRelatedElement (which will be a FeatureChain). - Object specific = obj.eGet(SysMLPackage.Literals.SPECIALIZATION__SPECIFIC, false); - if (specific == null) { - Element owner = obj.getOwningRelatedElement(); - if (owner instanceof Type) { - obj.setSpecific((Type)owner); - } else { - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setSpecific((Feature)ownedRelatedElements.get(0)); - } - } - } + getStructuralModelCompletionService().caseSpecialization(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/SubclassificationAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/SubclassificationAdapter.java index 4014d6398..2d11fb80b 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/SubclassificationAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/SubclassificationAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,10 +22,7 @@ package org.omg.sysml.adapter; -import org.omg.sysml.lang.sysml.Classifier; -import org.omg.sysml.lang.sysml.Element; import org.omg.sysml.lang.sysml.Subclassification; -import org.omg.sysml.lang.sysml.SysMLPackage; public class SubclassificationAdapter extends SpecializationAdapter { @@ -39,17 +37,7 @@ public Subclassification getTarget() { @Override public void postProcess() { - Subclassification obj = getTarget(); - - // If the subclassifier is empty, then set it to the owningRelatedElement, - // if this is a Classifier. - Object subclassifier = obj.eGet(SysMLPackage.Literals.SUBCLASSIFICATION__SUBCLASSIFIER, false); - if (subclassifier == null) { - Element owner = obj.getOwningRelatedElement(); - if (owner instanceof Classifier) { - obj.setSubclassifier((Classifier) owner); - } - } + getStructuralModelCompletionService().caseSubclassification(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/SubsettingAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/SubsettingAdapter.java index 7578e47e5..68b123f27 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/SubsettingAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/SubsettingAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,11 +22,7 @@ package org.omg.sysml.adapter; -import org.eclipse.emf.common.util.EList; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.Feature; import org.omg.sysml.lang.sysml.Subsetting; -import org.omg.sysml.lang.sysml.SysMLPackage; public class SubsettingAdapter extends SpecializationAdapter { @@ -40,32 +37,7 @@ public Subsetting getTarget() { @Override public void postProcess() { - Subsetting obj = getTarget(); - - // If the subsettedFeature is empty, then set it to the last ownedRelatedElement - // (which will be a Feature chain). - Object subsettedFeature = obj.eGet(SysMLPackage.Literals.SUBSETTING__SUBSETTED_FEATURE, false); - if (subsettedFeature == null) { - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setSubsettedFeature((Feature)ownedRelatedElements.get(ownedRelatedElements.size() - 1)); - } - } - - // If the subsettingFeature is empty, then set it to the owningRelatedFeature, if this is a Feature. - // Otherwise set it to the first ownedRelatedElement (which will be a Feature chain). - Object subsettingFeature = obj.eGet(SysMLPackage.Literals.SUBSETTING__SUBSETTING_FEATURE, false); - if (subsettingFeature == null) { - Element owner = obj.getOwningRelatedElement(); - if (owner instanceof Feature) { - obj.setSubsettingFeature((Feature) owner); - } else { - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setSubsettingFeature((Feature)ownedRelatedElements.get(0)); - } - } - } + getStructuralModelCompletionService().caseSubsetting(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/TextualRepresentationAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/TextualRepresentationAdapter.java index 7d8c67864..cdf031d90 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/TextualRepresentationAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/TextualRepresentationAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2026 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -19,7 +20,6 @@ package org.omg.sysml.adapter; import org.omg.sysml.lang.sysml.TextualRepresentation; -import org.omg.sysml.util.ElementUtil; public class TextualRepresentationAdapter extends AnnotatingElementAdapter { @@ -35,8 +35,6 @@ public TextualRepresentation getTarget() { @Override public void postProcess() { super.postProcess(); - TextualRepresentation target = getTarget(); - target.setLanguage(ElementUtil.unescapeString(target.getLanguage())); - target.setBody(ElementUtil.processCommentBody(target.getBody())); + getLexicalNormalizationService().caseTextualRepresentation(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/TypeFeaturingAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/TypeFeaturingAdapter.java index 6d539361f..17838ae5c 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/TypeFeaturingAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/TypeFeaturingAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,10 +22,7 @@ package org.omg.sysml.adapter; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.Feature; import org.omg.sysml.lang.sysml.TypeFeaturing; -import org.omg.sysml.lang.sysml.SysMLPackage; public class TypeFeaturingAdapter extends RelationshipAdapter { @@ -39,16 +37,7 @@ public TypeFeaturing getTarget() { @Override public void postProcess() { - TypeFeaturing obj = getTarget(); - - // If the featureOfType is empty, then set it to the owningRelatedElement (if this is a Feature). - Object featureOfType = obj.eGet(SysMLPackage.Literals.TYPE_FEATURING__FEATURE_OF_TYPE, false); - if (featureOfType == null) { - Element owner = obj.getOwningRelatedElement(); - if (owner instanceof Feature) { - obj.setFeatureOfType((Feature)owner); - } - } + getStructuralModelCompletionService().caseTypeFeaturing(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/UnioningAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/UnioningAdapter.java index 21c49b75d..4f7854ded 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/UnioningAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/UnioningAdapter.java @@ -2,6 +2,7 @@ * SysML 2 Pilot Implementation * Copyright (c) 2024 Model Driven Solutions, Inc. * Copyright (c) 2024 Budapest University of Technology and Economics + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -21,11 +22,7 @@ package org.omg.sysml.adapter; -import org.eclipse.emf.common.util.EList; -import org.omg.sysml.lang.sysml.Element; -import org.omg.sysml.lang.sysml.Feature; import org.omg.sysml.lang.sysml.Unioning; -import org.omg.sysml.lang.sysml.SysMLPackage; public class UnioningAdapter extends RelationshipAdapter { @@ -40,19 +37,7 @@ public Unioning getTarget() { @Override public void postProcess() { - Unioning obj = getTarget(); - - // If a Unioning is parsed targeting a Feature chain, then the unioningType will be empty, - // but the Unioning will own the unioningType. So, in this case, the unioningType should - // be set to the (last) ownedRelatedelement. - Object unioningType = obj.eGet(SysMLPackage.Literals.UNIONING__UNIONING_TYPE, false); - if (unioningType == null) { - // Handle a intersectingType that is a Feature chain. - EList ownedRelatedElements = obj.getOwnedRelatedElement(); - if (!ownedRelatedElements.isEmpty()) { - obj.setUnioningType((Feature)ownedRelatedElements.get(ownedRelatedElements.size() - 1)); - } - } + getStructuralModelCompletionService().caseUnioning(getTarget()); } } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/UsageAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/UsageAdapter.java index 97f23f2d4..099f3c44b 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/UsageAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/UsageAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2021-2025, 2026 Model Driven Solutions, Inc. + * Copyright (c) 2026 Obeo * * This program is free software: you can redistribute it and/or modify * it under the terms of the Eclipse Public License as published by @@ -34,8 +35,8 @@ import org.omg.sysml.lang.sysml.SysMLPackage; import org.omg.sysml.lang.sysml.Type; import org.omg.sysml.lang.sysml.Usage; -import org.omg.sysml.util.SysMLLibraryUtil; import org.omg.sysml.util.FeatureUtil; +import org.omg.sysml.util.SysMLLibraryUtil; import org.omg.sysml.util.TypeUtil; import org.omg.sysml.util.UsageUtil; @@ -58,13 +59,7 @@ public Usage getTarget() { @Override public void postProcess () { super.postProcess(); - Usage target = getTarget(); - if (target.isVariation()) { - target.setIsAbstract(true); - } - if (target.getDirection() != null || target.isEnd() || !UsageUtil.hasFeaturingType(target)) { - target.setIsComposite(false); - } + getStructuralModelCompletionService().caseUsage(getTarget()); } @Override diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/logic/IReferenceSetChecker.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/logic/IReferenceSetChecker.java new file mode 100644 index 000000000..2ff8d8171 --- /dev/null +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/logic/IReferenceSetChecker.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * SysML 2 Pilot Implementation + * Copyright (c) 2026 Obeo + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.omg.sysml.logic; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; + +/** + * Determines whether a reference has been explicitly set. + */ +@FunctionalInterface +public interface IReferenceSetChecker { + + /** + * Tests whether the given reference has been explicitly set on an object. + * + * @param object + * the object holding the reference + * @param reference + * the reference to test + * @return {@code true} if the reference has been explicitly set + */ + boolean isSet(EObject object, EReference reference); +} diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/logic/LexicalNormalizationService.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/logic/LexicalNormalizationService.java new file mode 100644 index 000000000..1d9ca4d65 --- /dev/null +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/logic/LexicalNormalizationService.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * SysML 2 Pilot Implementation + * Copyright (c) 2026 Obeo + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.omg.sysml.logic; + +import org.omg.sysml.lang.sysml.Comment; +import org.omg.sysml.lang.sysml.Element; +import org.omg.sysml.lang.sysml.LiteralString; +import org.omg.sysml.lang.sysml.Membership; +import org.omg.sysml.lang.sysml.OwningMembership; +import org.omg.sysml.lang.sysml.TextualRepresentation; +import org.omg.sysml.lang.sysml.util.SysMLSwitch; +import org.omg.sysml.util.ElementUtil; + +/** + * Normalizes lexical values produced by parsing SysML text. + */ +public class LexicalNormalizationService extends SysMLSwitch { + + private static final Boolean STOP_SUPERTYPE_PROCESSING = Boolean.TRUE; + + /** + * Normalizes the lexical values of one element. + * + * @param element + * the element to normalize + */ + public void normalize(Element element) { + if (element != null) { + doSwitch(element); + } + } + + @Override + public Boolean caseElement(Element object) { + // Source: org.omg.sysml.adapter.ElementAdapter#postProcess(). + // Normalize the names common to every element reached through the adapter hierarchy. + object.setDeclaredName(ElementUtil.unescapeString(object.getDeclaredName())); + object.setDeclaredShortName(ElementUtil.unescapeString(object.getDeclaredShortName())); + // ElementAdapter is the root of the post-processing hierarchy. + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseMembership(Membership object) { + // Source: org.omg.sysml.adapter.MembershipAdapter#postProcess(). + // Owning memberships derive their member names and therefore have no lexical names to normalize. + if (!(object instanceof OwningMembership)) { + object.setMemberName(ElementUtil.unescapeString(object.getMemberName())); + object.setMemberShortName(ElementUtil.unescapeString(object.getMemberShortName())); + } + // MembershipAdapter calls ElementAdapter.postProcess(). + return null; + } + + @Override + public Boolean caseComment(Comment object) { + // Source: org.omg.sysml.adapter.CommentAdapter#postProcess(). + // Normalize the optional locale and remove the concrete comment delimiters and indentation. + object.setLocale(ElementUtil.unescapeString(object.getLocale())); + object.setBody(ElementUtil.processCommentBody(object.getBody())); + // CommentAdapter calls its super adapter, which ultimately calls ElementAdapter. + return null; + } + + @Override + public Boolean caseTextualRepresentation(TextualRepresentation object) { + // Source: org.omg.sysml.adapter.TextualRepresentationAdapter#postProcess(). + // Normalize the language and process the textual body like a comment body. + object.setLanguage(ElementUtil.unescapeString(object.getLanguage())); + object.setBody(ElementUtil.processCommentBody(object.getBody())); + // TextualRepresentationAdapter calls its super adapter, which ultimately calls ElementAdapter. + return null; + } + + @Override + public Boolean caseLiteralString(LiteralString object) { + // Source: org.omg.sysml.adapter.LiteralStringAdapter#postProcess(). + // Convert the parsed string literal to its unescaped semantic value. + object.setValue(ElementUtil.unescapeString(object.getValue())); + // LiteralStringAdapter calls its super adapter, which ultimately calls ElementAdapter. + return null; + } +} diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/logic/StructuralModelCompletionService.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/logic/StructuralModelCompletionService.java new file mode 100644 index 000000000..9d65d34e4 --- /dev/null +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/logic/StructuralModelCompletionService.java @@ -0,0 +1,550 @@ +/******************************************************************************* + * SysML 2 Pilot Implementation + * Copyright (c) 2026 Obeo + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.omg.sysml.logic; + +import java.util.Objects; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.util.InternalEList; +import org.omg.sysml.lang.sysml.AnnotatingElement; +import org.omg.sysml.lang.sysml.Annotation; +import org.omg.sysml.lang.sysml.Classifier; +import org.omg.sysml.lang.sysml.ConjugatedPortDefinition; +import org.omg.sysml.lang.sysml.Conjugation; +import org.omg.sysml.lang.sysml.Definition; +import org.omg.sysml.lang.sysml.Dependency; +import org.omg.sysml.lang.sysml.Differencing; +import org.omg.sysml.lang.sysml.Disjoining; +import org.omg.sysml.lang.sysml.Element; +import org.omg.sysml.lang.sysml.EndFeatureMembership; +import org.omg.sysml.lang.sysml.Feature; +import org.omg.sysml.lang.sysml.FeatureInverting; +import org.omg.sysml.lang.sysml.FeatureMembership; +import org.omg.sysml.lang.sysml.FeatureTyping; +import org.omg.sysml.lang.sysml.Import; +import org.omg.sysml.lang.sysml.Intersecting; +import org.omg.sysml.lang.sysml.Namespace; +import org.omg.sysml.lang.sysml.NamespaceImport; +import org.omg.sysml.lang.sysml.OccurrenceUsage; +import org.omg.sysml.lang.sysml.ParameterMembership; +import org.omg.sysml.lang.sysml.PortConjugation; +import org.omg.sysml.lang.sysml.PortDefinition; +import org.omg.sysml.lang.sysml.PortUsage; +import org.omg.sysml.lang.sysml.Redefinition; +import org.omg.sysml.lang.sysml.ReferenceSubsetting; +import org.omg.sysml.lang.sysml.Relationship; +import org.omg.sysml.lang.sysml.Specialization; +import org.omg.sysml.lang.sysml.Subclassification; +import org.omg.sysml.lang.sysml.Subsetting; +import org.omg.sysml.lang.sysml.SysMLPackage; +import org.omg.sysml.lang.sysml.Type; +import org.omg.sysml.lang.sysml.TypeFeaturing; +import org.omg.sysml.lang.sysml.Unioning; +import org.omg.sysml.lang.sysml.Usage; +import org.omg.sysml.lang.sysml.VisibilityKind; +import org.omg.sysml.lang.sysml.util.SysMLSwitch; + +/** + * Completes structural model features that were omitted from the concrete + * syntax or that can be propagated from the containment structure. + *

+ * This service processes one element at a time and deliberately does not own a + * traversal strategy. It only reads unresolved references and containment + * links, so callers may use it as soon as the parsed containment graph is + * complete, before link resolution. The Xtext pipeline may also invoke the same + * cases later through its historical adapter post-processing phase. + *

+ */ +public class StructuralModelCompletionService extends SysMLSwitch { + + private static final Boolean STOP_SUPERTYPE_PROCESSING = Boolean.TRUE; + + private final IReferenceSetChecker referenceSetChecker; + + /** + * Creates a service that checks the current, unresolved EMF value of each + * reference. This preserves the historical adapter post-processing behavior. + */ + public StructuralModelCompletionService() { + this((object, reference) -> object.eGet(reference, false) != null); + } + + public StructuralModelCompletionService(IReferenceSetChecker referenceSetChecker) { + this.referenceSetChecker = Objects.requireNonNull(referenceSetChecker); + } + + /** + * Completes the structural features of one element. + * + * @param element + * the element to process + */ + public void complete(Element element) { + if (element != null) { + doSwitch(element); + } + } + + @Override + public Boolean caseFeature(Feature object) { + // Source: org.omg.sysml.adapter.FeatureAdapter#postProcess(). + // A constant Feature is variable, except for Usage, whose adapter overrides + // setIsVariableIfConstant() with a deliberate no-op. + if (object instanceof Usage) { + // Some multiple-inheritance switch paths (notably InterfaceUsage) visit + // Feature before Usage. Continue so caseUsage can apply the right rules. + return null; + } + if (object.isConstant()) { + object.setIsVariable(true); + } + // FeatureAdapter calls super.postProcess(). + return null; + } + + @Override + public Boolean caseUsage(Usage object) { + // Source: org.omg.sysml.adapter.UsageAdapter#postProcess(). + // Propagate syntax flags that constrain abstraction and composition. + if (object.isVariation()) { + object.setIsAbstract(true); + } + if (object.getDirection() != null || object.isEnd() || getExpectedFeaturingTypeOf(object) == null) { + object.setIsComposite(false); + } + // UsageAdapter calls super.postProcess(). caseFeature will therefore be + // reached on ordinary Usage switch paths, but its Usage guard reproduces the + // adapter's no-op setIsVariableIfConstant() override. + return null; + } + + @Override + public Boolean caseOccurrenceUsage(OccurrenceUsage object) { + // Source: org.omg.sysml.adapter.OccurrenceUsageAdapter#postProcess(). + // The Java adapter hierarchy deliberately excludes this behavior from + // PortUsageAdapter, although PortUsage inherits OccurrenceUsage in Ecore. + if (!(object instanceof PortUsage) && object.getPortionKind() != null) { + object.setIsPortion(true); + } + // Continue to Usage, matching OccurrenceUsageAdapter's super call. The two + // propagations write independent flags, so the switch order is immaterial. + return null; + } + + @Override + public Boolean caseDefinition(Definition object) { + // Source: org.omg.sysml.adapter.DefinitionAdapter#postProcess(). + // Variations cannot be concrete definitions. + if (object.isVariation()) { + object.setIsAbstract(true); + } + // DefinitionAdapter calls super.postProcess(). + return null; + } + + @Override + public Boolean caseParameterMembership(ParameterMembership object) { + // Source: org.omg.sysml.adapter.ParameterMembershipAdapter#postProcess(). + // Read the owned Feature from containment instead of the resolving derived + // getOwnedMemberParameter() reference. + Feature parameter = firstOwnedRelatedElement(object, Feature.class); + if (parameter != null) { + parameter.setDirection(object.parameterDirection()); + } + // ParameterMembershipAdapter calls super.postProcess(). + return null; + } + + @Override + public Boolean caseEndFeatureMembership(EndFeatureMembership object) { + // Source: org.omg.sysml.adapter.EndFeatureMembershipAdapter#postProcess(). + // Read the owned Feature from containment instead of the resolving derived + // getOwnedMemberFeature() reference. + Feature endFeature = firstOwnedRelatedElement(object, Feature.class); + if (endFeature != null) { + endFeature.setIsEnd(true); + } + // EndFeatureMembershipAdapter calls super.postProcess(). + return null; + } + + @Override + public Boolean casePortUsage(PortUsage object) { + // Source: org.omg.sysml.adapter.PortUsageAdapter#postProcess(). + // Determine the expected featuring type through containment, including for a + // variant nested in a variation Usage. + Type featuringType = getExpectedFeaturingTypeOf(object); + if (!(featuringType instanceof PortDefinition || featuringType instanceof PortUsage)) { + object.setIsComposite(false); + } + // Continue through OccurrenceUsage (where PortUsage is explicitly skipped) + // and then Usage, reproducing the Java PortUsageAdapter -> UsageAdapter chain. + return null; + } + + /** + * Determines the featuring type expected after transformation without resolving + * derived references. A Usage is featured when it is owned through a + * FeatureMembership, or when it is nested in a variation Usage that is itself + * featured. + */ + private Type getExpectedFeaturingTypeOf(Usage usage) { + EObject container = usage.eContainer(); + if (container instanceof FeatureMembership membership) { + Element owner = membership.getOwningRelatedElement(); + return owner instanceof Type type ? type : null; + } else if (container != null && container.eContainer() instanceof Usage containingUsage + && containingUsage.isVariation()) { + return getExpectedFeaturingTypeOf(containingUsage); + } else { + return null; + } + } + + @Override + public Boolean caseImport(Import object) { + // Source: org.omg.sysml.adapter.ImportAdapter#postProcess(). + // An import of a namespace owned through a NamespaceImport is a filtered + // package import and must be public. Both links below are containments. + if (object.getOwningRelatedElement() instanceof Namespace owningNamespace + && owningNamespace.getOwningRelationship() instanceof NamespaceImport) { + object.setVisibility(VisibilityKind.PUBLIC); + } + // ImportAdapter calls super.postProcess(). + return null; + } + + @Override + public Boolean caseDependency(Dependency object) { + // Source: org.omg.sysml.adapter.DependencyAdapter#postProcess(). + // Materialize the suppliers represented as owned related elements. Use the + // non-resolving InternalEList API: EObjectResolvingEList#contains may resolve + // proxies, while addUnique alone does not check uniqueness. + @SuppressWarnings("unchecked") + InternalEList suppliers = (InternalEList)object.getSupplier(); + for (Element supplier : object.getOwnedRelatedElement()) { + if (!suppliers.basicContains(supplier)) { + suppliers.addUnique(supplier); + } + } + // DependencyAdapter does not call super.postProcess(). + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseTypeFeaturing(TypeFeaturing object) { + // Source: org.omg.sysml.adapter.TypeFeaturingAdapter#postProcess(). + // Infer featureOfType from the owning related Feature when it was omitted. + if (isNotSet(object, SysMLPackage.Literals.TYPE_FEATURING__FEATURE_OF_TYPE) + && object.getOwningRelatedElement() instanceof Feature feature) { + object.setFeatureOfType(feature); + } + // The source adapter does not call super.postProcess(). + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseDifferencing(Differencing object) { + // Source: org.omg.sysml.adapter.DifferencingAdapter#postProcess(). + // Infer a feature-chain target from the last owned related element. + if (isNotSet(object, SysMLPackage.Literals.DIFFERENCING__DIFFERENCING_TYPE)) { + Feature target = lastOwnedRelatedElement(object, Feature.class); + if (target != null) { + object.setDifferencingType(target); + } + } + // The source adapter does not call super.postProcess(). + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean casePortConjugation(PortConjugation object) { + // Source: org.omg.sysml.adapter.PortConjugationAdapter#postProcess(). + // Infer the original port from the owner of the conjugated port definition. + if (isNotSet(object, SysMLPackage.Literals.PORT_CONJUGATION__ORIGINAL_PORT_DEFINITION)) { + ConjugatedPortDefinition conjugatedPortDefinition = unresolvedReference(object, + SysMLPackage.Literals.PORT_CONJUGATION__CONJUGATED_PORT_DEFINITION, + ConjugatedPortDefinition.class); + Element owner = owningRelatedElementOf(conjugatedPortDefinition); + if (owner instanceof PortDefinition original) { + object.setOriginalPortDefinition(original); + } + } + // Returning null reproduces the source adapter's call to ConjugationAdapter. + return null; + } + + @Override + public Boolean caseNamespaceImport(NamespaceImport object) { + // Source: org.omg.sysml.adapter.NamespaceImportAdapter#postProcess(). + // Infer an implicit filter-package import, or fall back to the owning namespace. + if (isNotSet(object, SysMLPackage.Literals.NAMESPACE_IMPORT__IMPORTED_NAMESPACE)) { + EList ownedRelatedElements = object.getOwnedRelatedElement(); + if (!ownedRelatedElements.isEmpty() && ownedRelatedElements.get(0) instanceof Namespace namespace) { + object.setImportedNamespace(namespace); + } else if (object.getOwningRelatedElement() instanceof Namespace owningNamespace) { + // getImportOwningNamespace() is derived through this containment but its + // generated getter requests proxy resolution. + object.setImportedNamespace(owningNamespace); + } + } + // Returning null reproduces the source adapter's call to ImportAdapter. + return null; + } + + @Override + public Boolean caseSubsetting(Subsetting object) { + // Source: org.omg.sysml.adapter.SubsettingAdapter#postProcess(). + // Infer the target from the end of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.SUBSETTING__SUBSETTED_FEATURE)) { + Feature target = lastOwnedRelatedElement(object, Feature.class); + if (target != null) { + object.setSubsettedFeature(target); + } + } + // Infer the source from the owner, or from the start of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.SUBSETTING__SUBSETTING_FEATURE)) { + Element owner = object.getOwningRelatedElement(); + Feature source = owner instanceof Feature feature ? feature : firstOwnedRelatedElement(object, Feature.class); + if (source != null) { + object.setSubsettingFeature(source); + } + } + // The source adapter intentionally bypasses SpecializationAdapter. + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseAnnotation(Annotation object) { + // Source: org.omg.sysml.adapter.AnnotationAdapter#postProcess(). + // Infer the annotated element from the relationship owner when applicable. + if (isNotSet(object, SysMLPackage.Literals.ANNOTATION__ANNOTATED_ELEMENT)) { + Element owner = object.getOwningRelatedElement(); + if (!(owner instanceof AnnotatingElement)) { + object.setAnnotatedElement(owner); + } + } + // The source adapter does not call super.postProcess(). + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseRedefinition(Redefinition object) { + // Source: org.omg.sysml.adapter.RedefinitionAdapter#postProcess(). + // Infer the redefining feature from the owner or the start of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.REDEFINITION__REDEFINING_FEATURE)) { + Element owner = object.getOwningRelatedElement(); + Feature source = owner instanceof Feature feature ? feature : firstOwnedRelatedElement(object, Feature.class); + if (source != null) { + object.setRedefiningFeature(source); + } + } + // Infer the redefined feature from the end of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.REDEFINITION__REDEFINED_FEATURE)) { + Feature target = lastOwnedRelatedElement(object, Feature.class); + if (target != null) { + object.setRedefinedFeature(target); + } + } + // The source adapter intentionally bypasses SubsettingAdapter and its supertype. + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseUnioning(Unioning object) { + // Source: org.omg.sysml.adapter.UnioningAdapter#postProcess(). + // Infer a feature-chain target from the last owned related element. + if (isNotSet(object, SysMLPackage.Literals.UNIONING__UNIONING_TYPE)) { + Feature target = lastOwnedRelatedElement(object, Feature.class); + if (target != null) { + object.setUnioningType(target); + } + } + // The source adapter does not call super.postProcess(). + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseFeatureInverting(FeatureInverting object) { + // Source: org.omg.sysml.adapter.FeatureInvertingAdapter#postProcess(). + // Infer the inverted feature from the owner or the start of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.FEATURE_INVERTING__FEATURE_INVERTED)) { + Element owner = object.getOwningRelatedElement(); + Feature source = owner instanceof Feature feature ? feature : firstOwnedRelatedElement(object, Feature.class); + if (source != null) { + object.setFeatureInverted(source); + } + } + // Infer the inverting feature from the end of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.FEATURE_INVERTING__INVERTING_FEATURE)) { + Feature target = lastOwnedRelatedElement(object, Feature.class); + if (target != null) { + object.setInvertingFeature(target); + } + } + // The source adapter does not call super.postProcess(). + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseReferenceSubsetting(ReferenceSubsetting object) { + // Source: org.omg.sysml.adapter.ReferenceSubsettingAdapter#postProcess(). + // Infer the referenced feature from the end of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.REFERENCE_SUBSETTING__REFERENCED_FEATURE)) { + Feature target = lastOwnedRelatedElement(object, Feature.class); + if (target != null) { + object.setReferencedFeature(target); + } + } + // The source adapter intentionally bypasses SubsettingAdapter and its supertype. + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseFeatureTyping(FeatureTyping object) { + // Source: org.omg.sysml.adapter.FeatureTypingAdapter#postProcess(). + // Infer the type from the start of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.FEATURE_TYPING__TYPE)) { + Feature target = firstOwnedRelatedElement(object, Feature.class); + if (target != null) { + object.setType(target); + } + } + // Infer the typed feature from the relationship owner. + if (isNotSet(object, SysMLPackage.Literals.FEATURE_TYPING__TYPED_FEATURE) + && object.getOwningRelatedElement() instanceof Feature feature) { + object.setTypedFeature(feature); + } + // The source adapter intentionally bypasses SpecializationAdapter. + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseConjugation(Conjugation object) { + // Source: org.omg.sysml.adapter.ConjugationAdapter#postProcess(). + // Infer the conjugated type from the owner or the start of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.CONJUGATION__CONJUGATED_TYPE)) { + Element owner = object.getOwningRelatedElement(); + Type source = owner instanceof Type type ? type : firstOwnedRelatedElement(object, Type.class); + if (source != null) { + object.setConjugatedType(source); + } + } + // Infer the original type from the end of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.CONJUGATION__ORIGINAL_TYPE)) { + Type target = lastOwnedRelatedElement(object, Type.class); + if (target != null) { + object.setOriginalType(target); + } + } + // The source adapter does not call super.postProcess(). + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseDisjoining(Disjoining object) { + // Source: org.omg.sysml.adapter.DisjoiningAdapter#postProcess(). + // Infer the disjoining type from the end of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.DISJOINING__DISJOINING_TYPE)) { + Feature target = lastOwnedRelatedElement(object, Feature.class); + if (target != null) { + object.setDisjoiningType(target); + } + } + // Infer the disjoined type from the owner or the start of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.DISJOINING__TYPE_DISJOINED)) { + Element owner = object.getOwningRelatedElement(); + Type source = owner instanceof Type type ? type : firstOwnedRelatedElement(object, Feature.class); + if (source != null) { + object.setTypeDisjoined(source); + } + } + // The source adapter does not call super.postProcess(). + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseIntersecting(Intersecting object) { + // Source: org.omg.sysml.adapter.IntersectingAdapter#postProcess(). + // Infer a feature-chain target from the last owned related element. + if (isNotSet(object, SysMLPackage.Literals.INTERSECTING__INTERSECTING_TYPE)) { + Feature target = lastOwnedRelatedElement(object, Feature.class); + if (target != null) { + object.setIntersectingType(target); + } + } + // The source adapter does not call super.postProcess(). + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseSubclassification(Subclassification object) { + // Source: org.omg.sysml.adapter.SubclassificationAdapter#postProcess(). + // Infer the subclassifier from the owning related Classifier. + if (isNotSet(object, SysMLPackage.Literals.SUBCLASSIFICATION__SUBCLASSIFIER) + && object.getOwningRelatedElement() instanceof Classifier classifier) { + object.setSubclassifier(classifier); + } + // The source adapter intentionally bypasses SpecializationAdapter. + return STOP_SUPERTYPE_PROCESSING; + } + + @Override + public Boolean caseSpecialization(Specialization object) { + // Source: org.omg.sysml.adapter.SpecializationAdapter#postProcess(). + // Infer the general type from the end of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.SPECIALIZATION__GENERAL)) { + Feature target = lastOwnedRelatedElement(object, Feature.class); + if (target != null) { + object.setGeneral(target); + } + } + // Infer the specific type from the owner or the start of a feature chain. + if (isNotSet(object, SysMLPackage.Literals.SPECIALIZATION__SPECIFIC)) { + Element owner = object.getOwningRelatedElement(); + Type source = owner instanceof Type type ? type : firstOwnedRelatedElement(object, Feature.class); + if (source != null) { + object.setSpecific(source); + } + } + // The source adapter does not call super.postProcess(). + return STOP_SUPERTYPE_PROCESSING; + } + + private boolean isNotSet(Element object, EReference reference) { + return !this.referenceSetChecker.isSet(object, reference); + } + + private T unresolvedReference(Element object, EReference reference, Class type) { + Object value = object.eGet(reference, false); + return type.isInstance(value) ? type.cast(value) : null; + } + + private Element owningRelatedElementOf(Element element) { + if (element != null && element.getOwningRelationship() != null) { + return element.getOwningRelationship().getOwningRelatedElement(); + } + return null; + } + + private T firstOwnedRelatedElement(Relationship relationship, Class type) { + EList elements = relationship.getOwnedRelatedElement(); + return elements.isEmpty() || !type.isInstance(elements.get(0)) ? null : type.cast(elements.get(0)); + } + + private T lastOwnedRelatedElement(Relationship relationship, Class type) { + EList elements = relationship.getOwnedRelatedElement(); + Element last = elements.isEmpty() ? null : elements.get(elements.size() - 1); + return type.isInstance(last) ? type.cast(last) : null; + } +} diff --git a/org.omg.sysml.logic/src/test/java/org/omg/sysml/logic/LexicalNormalizationServiceTest.java b/org.omg.sysml.logic/src/test/java/org/omg/sysml/logic/LexicalNormalizationServiceTest.java new file mode 100644 index 000000000..fd69a98a2 --- /dev/null +++ b/org.omg.sysml.logic/src/test/java/org/omg/sysml/logic/LexicalNormalizationServiceTest.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * SysML 2 Pilot Implementation + * Copyright (c) 2026 Obeo + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.omg.sysml.logic; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.omg.sysml.lang.sysml.Comment; +import org.omg.sysml.lang.sysml.LiteralString; +import org.omg.sysml.lang.sysml.Membership; +import org.omg.sysml.lang.sysml.OwningMembership; +import org.omg.sysml.lang.sysml.Package; +import org.omg.sysml.lang.sysml.SysMLFactory; +import org.omg.sysml.lang.sysml.SysMLPackage; +import org.omg.sysml.lang.sysml.TextualRepresentation; +import org.omg.sysml.util.ElementUtil; + +public class LexicalNormalizationServiceTest { + + private static final SysMLFactory FACTORY = SysMLFactory.eINSTANCE; + + private final LexicalNormalizationService service = new LexicalNormalizationService(); + + @BeforeClass + public static void setUp() { + SysMLLogicStandaloneSetup.doSetup(); + SysMLPackage.eINSTANCE.eClass(); + } + + @Test + public void normalizesElementNames() { + Package element = FACTORY.createPackage(); + element.setDeclaredName("'declared name'"); + element.setDeclaredShortName("'short name'"); + + this.service.normalize(element); + + assertEquals("declared name", element.getDeclaredName()); + assertEquals("short name", element.getDeclaredShortName()); + } + + @Test + public void normalizesNonOwningMembershipNames() { + Membership membership = FACTORY.createMembership(); + membership.setDeclaredName("'membership'"); + membership.setMemberName("'member name'"); + membership.setMemberShortName("'member short name'"); + + this.service.normalize(membership); + + assertEquals("membership", membership.getDeclaredName()); + assertEquals("member name", membership.getMemberName()); + assertEquals("member short name", membership.getMemberShortName()); + } + + @Test + public void preservesOwningMembershipNames() { + OwningMembership membership = FACTORY.createOwningMembership(); + Package ownedMember = FACTORY.createPackage(); + ownedMember.setDeclaredName("'member name'"); + ownedMember.setDeclaredShortName("'member short name'"); + membership.getOwnedRelatedElement().add(ownedMember); + + this.service.normalize(membership); + + assertEquals("'member name'", membership.getMemberName()); + assertEquals("'member short name'", membership.getMemberShortName()); + } + + @Test + public void normalizesComment() { + Comment comment = FACTORY.createComment(); + comment.setLocale("'en-US'"); + comment.setBody("/*first\n*second*/"); + + this.service.normalize(comment); + + assertEquals("en-US", comment.getLocale()); + assertEquals("first\nsecond", comment.getBody()); + } + + @Test + public void normalizesTextualRepresentation() { + TextualRepresentation representation = FACTORY.createTextualRepresentation(); + representation.setLanguage("'KerML'"); + representation.setBody("/*body*/"); + + this.service.normalize(representation); + + assertEquals("KerML", representation.getLanguage()); + assertEquals("body", representation.getBody()); + } + + @Test + public void normalizesLiteralString() { + LiteralString literal = FACTORY.createLiteralString(); + literal.setValue("\"first\\nsecond\""); + + this.service.normalize(literal); + + assertEquals("first\nsecond", literal.getValue()); + } + + @Test + public void acceptsNullValuesAndElement() { + Package element = FACTORY.createPackage(); + + this.service.normalize(null); + this.service.normalize(element); + + assertNull(element.getDeclaredName()); + assertNull(element.getDeclaredShortName()); + } + + @Test + public void adaptersDelegateToTheirSpecificNormalizationCases() { + Package element = FACTORY.createPackage(); + element.setDeclaredName("'element'"); + ElementUtil.postProcess(element); + assertEquals("element", element.getDeclaredName()); + + Membership membership = FACTORY.createMembership(); + membership.setDeclaredName("'membership'"); + membership.setMemberName("'member'"); + ElementUtil.postProcess(membership); + assertEquals("membership", membership.getDeclaredName()); + assertEquals("member", membership.getMemberName()); + + Comment comment = FACTORY.createComment(); + comment.setDeclaredName("'comment'"); + comment.setLocale("'fr'"); + comment.setBody("/*body*/"); + ElementUtil.postProcess(comment); + assertEquals("comment", comment.getDeclaredName()); + assertEquals("fr", comment.getLocale()); + assertEquals("body", comment.getBody()); + + TextualRepresentation representation = FACTORY.createTextualRepresentation(); + representation.setDeclaredName("'representation'"); + representation.setLanguage("'SysML'"); + representation.setBody("/*text*/"); + ElementUtil.postProcess(representation); + assertEquals("representation", representation.getDeclaredName()); + assertEquals("SysML", representation.getLanguage()); + assertEquals("text", representation.getBody()); + + LiteralString literal = FACTORY.createLiteralString(); + literal.setDeclaredName("'literal'"); + literal.setValue("\"value\""); + ElementUtil.postProcess(literal); + assertEquals("literal", literal.getDeclaredName()); + assertEquals("value", literal.getValue()); + } +} diff --git a/org.omg.sysml.logic/src/test/java/org/omg/sysml/logic/StructuralModelCompletionServiceTest.java b/org.omg.sysml.logic/src/test/java/org/omg/sysml/logic/StructuralModelCompletionServiceTest.java new file mode 100644 index 000000000..d3c043eaa --- /dev/null +++ b/org.omg.sysml.logic/src/test/java/org/omg/sysml/logic/StructuralModelCompletionServiceTest.java @@ -0,0 +1,487 @@ +/******************************************************************************* + * SysML 2 Pilot Implementation + * Copyright (c) 2026 Obeo + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.omg.sysml.logic; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.InternalEObject; +import org.eclipse.emf.ecore.util.InternalEList; +import org.junit.BeforeClass; +import org.junit.Test; +import org.omg.sysml.lang.sysml.Annotation; +import org.omg.sysml.lang.sysml.ConjugatedPortDefinition; +import org.omg.sysml.lang.sysml.Conjugation; +import org.omg.sysml.lang.sysml.Dependency; +import org.omg.sysml.lang.sysml.Differencing; +import org.omg.sysml.lang.sysml.Disjoining; +import org.omg.sysml.lang.sysml.Element; +import org.omg.sysml.lang.sysml.EndFeatureMembership; +import org.omg.sysml.lang.sysml.Feature; +import org.omg.sysml.lang.sysml.FeatureDirectionKind; +import org.omg.sysml.lang.sysml.FeatureInverting; +import org.omg.sysml.lang.sysml.FeatureMembership; +import org.omg.sysml.lang.sysml.FeatureTyping; +import org.omg.sysml.lang.sysml.InterfaceUsage; +import org.omg.sysml.lang.sysml.Intersecting; +import org.omg.sysml.lang.sysml.NamespaceImport; +import org.omg.sysml.lang.sysml.OccurrenceUsage; +import org.omg.sysml.lang.sysml.OwningMembership; +import org.omg.sysml.lang.sysml.Package; +import org.omg.sysml.lang.sysml.ParameterMembership; +import org.omg.sysml.lang.sysml.PartDefinition; +import org.omg.sysml.lang.sysml.PartUsage; +import org.omg.sysml.lang.sysml.PortConjugation; +import org.omg.sysml.lang.sysml.PortDefinition; +import org.omg.sysml.lang.sysml.PortUsage; +import org.omg.sysml.lang.sysml.PortionKind; +import org.omg.sysml.lang.sysml.Redefinition; +import org.omg.sysml.lang.sysml.ReferenceSubsetting; +import org.omg.sysml.lang.sysml.Relationship; +import org.omg.sysml.lang.sysml.Specialization; +import org.omg.sysml.lang.sysml.Subclassification; +import org.omg.sysml.lang.sysml.Subsetting; +import org.omg.sysml.lang.sysml.SysMLFactory; +import org.omg.sysml.lang.sysml.SysMLPackage; +import org.omg.sysml.lang.sysml.TypeFeaturing; +import org.omg.sysml.lang.sysml.Unioning; +import org.omg.sysml.lang.sysml.VariantMembership; +import org.omg.sysml.lang.sysml.VisibilityKind; +import org.omg.sysml.util.ElementUtil; + +public class StructuralModelCompletionServiceTest { + + private static final SysMLFactory FACTORY = SysMLFactory.eINSTANCE; + + private final StructuralModelCompletionService service = new StructuralModelCompletionService( + (object, reference) -> object.eGet(reference, false) != null); + + @BeforeClass + public static void setUp() { + SysMLLogicStandaloneSetup.doSetup(); + SysMLPackage.eINSTANCE.eClass(); + } + + @Test + public void infersLastFeatureTargets() { + Differencing differencing = FACTORY.createDifferencing(); + Feature differencingTarget = addFeatureTarget(differencing); + this.service.complete(differencing); + assertSame(differencingTarget, differencing.getDifferencingType()); + + Unioning unioning = FACTORY.createUnioning(); + Feature unioningTarget = addFeatureTarget(unioning); + this.service.complete(unioning); + assertSame(unioningTarget, unioning.getUnioningType()); + + Intersecting intersecting = FACTORY.createIntersecting(); + Feature intersectingTarget = addFeatureTarget(intersecting); + this.service.complete(intersecting); + assertSame(intersectingTarget, intersecting.getIntersectingType()); + + ReferenceSubsetting referenceSubsetting = FACTORY.createReferenceSubsetting(); + Feature referencedFeature = addFeatureTarget(referenceSubsetting); + this.service.complete(referenceSubsetting); + assertSame(referencedFeature, referenceSubsetting.getReferencedFeature()); + } + + @Test + public void infersRelationshipEndsFromOwnerAndFeatureChain() { + Feature subsettingOwner = FACTORY.createFeature(); + Subsetting subsetting = ownRelationship(subsettingOwner, FACTORY.createSubsetting()); + Feature subsettedFeature = addFeatureTarget(subsetting); + this.service.complete(subsetting); + assertSame(subsettingOwner, subsetting.getSubsettingFeature()); + assertSame(subsettedFeature, subsetting.getSubsettedFeature()); + + Feature redefiningFeature = FACTORY.createFeature(); + Redefinition redefinition = ownRelationship(redefiningFeature, FACTORY.createRedefinition()); + Feature redefinedFeature = addFeatureTarget(redefinition); + this.service.complete(redefinition); + assertSame(redefiningFeature, redefinition.getRedefiningFeature()); + assertSame(redefinedFeature, redefinition.getRedefinedFeature()); + + Feature invertedFeature = FACTORY.createFeature(); + FeatureInverting inverting = ownRelationship(invertedFeature, FACTORY.createFeatureInverting()); + Feature invertingFeature = addFeatureTarget(inverting); + this.service.complete(inverting); + assertSame(invertedFeature, inverting.getFeatureInverted()); + assertSame(invertingFeature, inverting.getInvertingFeature()); + + PartDefinition disjoinedType = FACTORY.createPartDefinition(); + Disjoining disjoining = ownRelationship(disjoinedType, FACTORY.createDisjoining()); + Feature disjoiningType = addFeatureTarget(disjoining); + this.service.complete(disjoining); + assertSame(disjoinedType, disjoining.getTypeDisjoined()); + assertSame(disjoiningType, disjoining.getDisjoiningType()); + + PartDefinition specific = FACTORY.createPartDefinition(); + Specialization specialization = ownRelationship(specific, FACTORY.createSpecialization()); + Feature general = addFeatureTarget(specialization); + this.service.complete(specialization); + assertSame(specific, specialization.getSpecific()); + assertSame(general, specialization.getGeneral()); + + PartDefinition conjugated = FACTORY.createPartDefinition(); + Conjugation conjugation = ownRelationship(conjugated, FACTORY.createConjugation()); + PartDefinition original = FACTORY.createPartDefinition(); + conjugation.getOwnedRelatedElement().add(original); + this.service.complete(conjugation); + assertSame(conjugated, conjugation.getConjugatedType()); + assertSame(original, conjugation.getOriginalType()); + } + + @Test + public void infersOwnerAndFirstElementReferences() { + Feature typedFeature = FACTORY.createFeature(); + FeatureTyping featureTyping = ownRelationship(typedFeature, FACTORY.createFeatureTyping()); + Feature type = addFeatureTarget(featureTyping); + this.service.complete(featureTyping); + assertSame(typedFeature, featureTyping.getTypedFeature()); + assertSame(type, featureTyping.getType()); + + Feature featured = FACTORY.createFeature(); + TypeFeaturing typeFeaturing = ownRelationship(featured, FACTORY.createTypeFeaturing()); + this.service.complete(typeFeaturing); + assertSame(featured, typeFeaturing.getFeatureOfType()); + + PartDefinition annotated = FACTORY.createPartDefinition(); + Annotation annotation = ownRelationship(annotated, FACTORY.createAnnotation()); + this.service.complete(annotation); + assertSame(annotated, annotation.getAnnotatedElement()); + + Package importingNamespace = FACTORY.createPackage(); + NamespaceImport namespaceImport = ownRelationship(importingNamespace, FACTORY.createNamespaceImport()); + this.service.complete(namespaceImport); + assertSame(importingNamespace, namespaceImport.getImportedNamespace()); + + PartDefinition subclassifier = FACTORY.createPartDefinition(); + Subclassification subclassification = ownRelationship(subclassifier, FACTORY.createSubclassification()); + this.service.complete(subclassification); + assertSame(subclassifier, subclassification.getSubclassifier()); + } + + @Test + public void portConjugationContinuesWithConjugationInference() { + PortDefinition originalPort = FACTORY.createPortDefinition(); + ConjugatedPortDefinition conjugatedPort = FACTORY.createConjugatedPortDefinition(); + OwningMembership membership = ownRelationship(originalPort, FACTORY.createOwningMembership()); + membership.getOwnedRelatedElement().add(conjugatedPort); + PortConjugation portConjugation = ownRelationship(conjugatedPort, FACTORY.createPortConjugation()); + + this.service.complete(portConjugation); + + assertSame(originalPort, portConjugation.getOriginalPortDefinition()); + assertSame(conjugatedPort, portConjugation.getConjugatedType()); + } + + @Test + public void explicitReferencePreventsInference() { + ReferenceSubsetting referenceSubsetting = FACTORY.createReferenceSubsetting(); + addFeatureTarget(referenceSubsetting); + StructuralModelCompletionService explicitService = new StructuralModelCompletionService( + (object, reference) -> reference == SysMLPackage.Literals.REFERENCE_SUBSETTING__REFERENCED_FEATURE); + + explicitService.complete(referenceSubsetting); + + assertNull(referenceSubsetting.getReferencedFeature()); + } + + @Test + public void completesFeatureAndUsagePropertiesWithoutApplyingFeatureRuleToUsages() { + Feature feature = FACTORY.createFeature(); + feature.setIsConstant(true); + this.service.complete(feature); + assertTrue(feature.isVariable()); + + PartUsage usage = FACTORY.createPartUsage(); + usage.setIsConstant(true); + usage.setIsVariation(true); + usage.setDirection(FeatureDirectionKind.IN); + usage.setIsComposite(true); + this.service.complete(usage); + assertFalse(usage.isVariable()); + assertTrue(usage.isAbstract()); + assertFalse(usage.isComposite()); + + // InterfaceUsage visits Feature before Usage in the generated switch. This + // regression proves that the Feature guard preserves Usage semantics. + InterfaceUsage interfaceUsage = FACTORY.createInterfaceUsage(); + interfaceUsage.setIsConstant(true); + this.service.complete(interfaceUsage); + assertFalse(interfaceUsage.isVariable()); + } + + @Test + public void preservesNestedVariantCompositionWhenFeaturingTypeExists() { + PartDefinition owner = FACTORY.createPartDefinition(); + FeatureMembership variationMembership = ownRelationship(owner, FACTORY.createFeatureMembership()); + PartUsage variation = FACTORY.createPartUsage(); + variation.setIsVariation(true); + variation.setIsComposite(true); + variationMembership.getOwnedRelatedElement().add(variation); + + VariantMembership nestedVariationMembership = ownRelationship(variation, FACTORY.createVariantMembership()); + PartUsage nestedVariation = FACTORY.createPartUsage(); + nestedVariation.setIsVariation(true); + nestedVariation.setIsComposite(true); + nestedVariationMembership.getOwnedRelatedElement().add(nestedVariation); + + VariantMembership variantMembership = ownRelationship(nestedVariation, FACTORY.createVariantMembership()); + PartUsage variant = FACTORY.createPartUsage(); + variant.setIsComposite(true); + variantMembership.getOwnedRelatedElement().add(variant); + + this.service.complete(nestedVariation); + this.service.complete(variant); + + assertTrue(nestedVariation.isComposite()); + assertTrue(variant.isComposite()); + } + + @Test + public void clearsVariantCompositionWhenVariationHasNoFeaturingType() { + Package owner = FACTORY.createPackage(); + OwningMembership variationMembership = ownRelationship(owner, FACTORY.createOwningMembership()); + PartUsage variation = FACTORY.createPartUsage(); + variation.setIsVariation(true); + variationMembership.getOwnedRelatedElement().add(variation); + + VariantMembership variantMembership = ownRelationship(variation, FACTORY.createVariantMembership()); + PartUsage variant = FACTORY.createPartUsage(); + variant.setIsComposite(true); + variantMembership.getOwnedRelatedElement().add(variant); + + this.service.complete(variant); + + assertFalse(variant.isComposite()); + } + + @Test + public void completesOccurrenceAndDefinitionProperties() { + OccurrenceUsage occurrenceUsage = FACTORY.createOccurrenceUsage(); + occurrenceUsage.setPortionKind(PortionKind.SNAPSHOT); + this.service.complete(occurrenceUsage); + assertTrue(occurrenceUsage.isPortion()); + + PartDefinition definition = FACTORY.createPartDefinition(); + definition.setIsVariation(true); + this.service.complete(definition); + assertTrue(definition.isAbstract()); + } + + @Test + public void propagatesMembershipPropertiesFromOwnedElementsWithoutDerivedGetters() { + ParameterMembership parameterMembership = FACTORY.createParameterMembership(); + Feature parameter = FACTORY.createFeature(); + parameterMembership.getOwnedRelatedElement().add(parameter); + this.service.complete(parameterMembership); + assertEquals(FeatureDirectionKind.IN, parameter.getDirection()); + + EndFeatureMembership endMembership = FACTORY.createEndFeatureMembership(); + Feature endFeature = FACTORY.createFeature(); + endMembership.getOwnedRelatedElement().add(endFeature); + this.service.complete(endMembership); + assertTrue(endFeature.isEnd()); + } + + @Test + public void correctsPortUsageStructureWithoutOccurrencePropagation() { + PartDefinition owner = FACTORY.createPartDefinition(); + FeatureMembership membership = ownRelationship(owner, FACTORY.createFeatureMembership()); + PortUsage portUsage = FACTORY.createPortUsage(); + portUsage.setIsComposite(true); + portUsage.setPortionKind(PortionKind.SNAPSHOT); + membership.getOwnedRelatedElement().add(portUsage); + + this.service.complete(portUsage); + + assertFalse(portUsage.isComposite()); + assertFalse(portUsage.isPortion()); + } + + @Test + public void evaluatesVariantPortCompositionAgainstExpectedFeaturingType() { + PortUsage portOwner = FACTORY.createPortUsage(); + FeatureMembership portVariationMembership = ownRelationship(portOwner, FACTORY.createFeatureMembership()); + PortUsage portVariation = FACTORY.createPortUsage(); + portVariation.setIsVariation(true); + portVariationMembership.getOwnedRelatedElement().add(portVariation); + VariantMembership subportMembership = ownRelationship(portVariation, FACTORY.createVariantMembership()); + PortUsage subport = FACTORY.createPortUsage(); + subport.setIsComposite(true); + subportMembership.getOwnedRelatedElement().add(subport); + + PartUsage partOwner = FACTORY.createPartUsage(); + FeatureMembership partVariationMembership = ownRelationship(partOwner, FACTORY.createFeatureMembership()); + PartUsage partVariation = FACTORY.createPartUsage(); + partVariation.setIsVariation(true); + partVariationMembership.getOwnedRelatedElement().add(partVariation); + VariantMembership portMembership = ownRelationship(partVariation, FACTORY.createVariantMembership()); + PortUsage port = FACTORY.createPortUsage(); + port.setIsComposite(true); + portMembership.getOwnedRelatedElement().add(port); + + this.service.complete(subport); + this.service.complete(port); + + assertTrue(subport.isComposite()); + assertFalse(port.isComposite()); + } + + @Test + public void correctsFilteredImportVisibilityFromContainment() { + Package outerPackage = FACTORY.createPackage(); + NamespaceImport owningImport = ownRelationship(outerPackage, FACTORY.createNamespaceImport()); + Package filteredPackage = FACTORY.createPackage(); + owningImport.getOwnedRelatedElement().add(filteredPackage); + NamespaceImport nestedImport = ownRelationship(filteredPackage, FACTORY.createNamespaceImport()); + nestedImport.setVisibility(VisibilityKind.PRIVATE); + + this.service.complete(nestedImport); + + assertEquals(VisibilityKind.PUBLIC, nestedImport.getVisibility()); + } + + @Test + public void derivesDependencySuppliersIdempotently() { + Dependency dependency = FACTORY.createDependency(); + PartDefinition explicitSupplierProxy = FACTORY.createPartDefinition(); + ((InternalEObject)explicitSupplierProxy).eSetProxyURI(URI.createURI("unresolved:/supplier")); + PartDefinition structuralSupplier = FACTORY.createPartDefinition(); + dependency.getSupplier().add(explicitSupplierProxy); + dependency.getOwnedRelatedElement().add(structuralSupplier); + + this.service.complete(dependency); + this.service.complete(dependency); + + assertEquals(2, dependency.getSupplier().size()); + @SuppressWarnings("unchecked") + InternalEList suppliers = (InternalEList)dependency.getSupplier(); + assertSame(explicitSupplierProxy, suppliers.basicGet(0)); + assertTrue(suppliers.basicGet(0).eIsProxy()); + assertSame(structuralSupplier, suppliers.basicGet(1)); + } + + @Test + public void doesNotResolveExplicitProxyReferences() { + ReferenceSubsetting referenceSubsetting = FACTORY.createReferenceSubsetting(); + Feature proxy = FACTORY.createFeature(); + ((InternalEObject)proxy).eSetProxyURI(URI.createURI("unresolved:/feature")); + referenceSubsetting.setReferencedFeature(proxy); + + new StructuralModelCompletionService().complete(referenceSubsetting); + + Feature unresolved = (Feature)referenceSubsetting.eGet( + SysMLPackage.Literals.REFERENCE_SUBSETTING__REFERENCED_FEATURE, false); + assertSame(proxy, unresolved); + assertTrue(unresolved.eIsProxy()); + } + + @Test + public void specializedRelationshipsDoNotInferSupertypeReferences() { + ReferenceSubsetting referenceSubsetting = FACTORY.createReferenceSubsetting(); + addFeatureTarget(referenceSubsetting); + this.service.complete(referenceSubsetting); + assertFalse(referenceSubsetting.eIsSet(SysMLPackage.Literals.SUBSETTING__SUBSETTED_FEATURE)); + assertFalse(referenceSubsetting.eIsSet(SysMLPackage.Literals.SPECIALIZATION__GENERAL)); + + Redefinition redefinition = FACTORY.createRedefinition(); + addFeatureTarget(redefinition); + this.service.complete(redefinition); + assertFalse(redefinition.eIsSet(SysMLPackage.Literals.SUBSETTING__SUBSETTED_FEATURE)); + assertFalse(redefinition.eIsSet(SysMLPackage.Literals.SPECIALIZATION__GENERAL)); + + Subsetting subsetting = FACTORY.createSubsetting(); + addFeatureTarget(subsetting); + this.service.complete(subsetting); + assertFalse(subsetting.eIsSet(SysMLPackage.Literals.SPECIALIZATION__GENERAL)); + + FeatureTyping typing = FACTORY.createFeatureTyping(); + addFeatureTarget(typing); + this.service.complete(typing); + assertFalse(typing.eIsSet(SysMLPackage.Literals.SPECIALIZATION__GENERAL)); + + Subclassification subclassification = FACTORY.createSubclassification(); + this.service.complete(subclassification); + assertFalse(subclassification.eIsSet(SysMLPackage.Literals.SPECIALIZATION__GENERAL)); + } + + @Test + public void adapterPostProcessingDelegatesToTheSameSpecificCases() { + Feature constantFeature = FACTORY.createFeature(); + constantFeature.setIsConstant(true); + ElementUtil.postProcess(constantFeature); + assertTrue(constantFeature.isVariable()); + + OccurrenceUsage occurrenceUsage = FACTORY.createOccurrenceUsage(); + occurrenceUsage.setIsConstant(true); + occurrenceUsage.setPortionKind(PortionKind.SNAPSHOT); + ElementUtil.postProcess(occurrenceUsage); + assertFalse(occurrenceUsage.isVariable()); + assertTrue(occurrenceUsage.isPortion()); + + ParameterMembership parameterMembership = FACTORY.createParameterMembership(); + Feature parameter = FACTORY.createFeature(); + parameterMembership.getOwnedRelatedElement().add(parameter); + ElementUtil.postProcess(parameterMembership); + assertEquals(FeatureDirectionKind.IN, parameter.getDirection()); + + Dependency dependency = FACTORY.createDependency(); + PartDefinition supplier = FACTORY.createPartDefinition(); + dependency.getOwnedRelatedElement().add(supplier); + ElementUtil.postProcess(dependency); + ElementUtil.postProcess(dependency); + assertEquals(1, dependency.getSupplier().size()); + assertSame(supplier, dependency.getSupplier().get(0)); + + ReferenceSubsetting referenceSubsetting = FACTORY.createReferenceSubsetting(); + Feature referencedFeature = addFeatureTarget(referenceSubsetting); + ElementUtil.postProcess(referenceSubsetting); + assertSame(referencedFeature, referenceSubsetting.getReferencedFeature()); + assertFalse(referenceSubsetting.eIsSet(SysMLPackage.Literals.SUBSETTING__SUBSETTED_FEATURE)); + + Redefinition redefinition = FACTORY.createRedefinition(); + Feature redefinedFeature = addFeatureTarget(redefinition); + ElementUtil.postProcess(redefinition); + assertSame(redefinedFeature, redefinition.getRedefinedFeature()); + assertFalse(redefinition.eIsSet(SysMLPackage.Literals.SUBSETTING__SUBSETTED_FEATURE)); + + PortDefinition originalPort = FACTORY.createPortDefinition(); + ConjugatedPortDefinition conjugatedPort = FACTORY.createConjugatedPortDefinition(); + OwningMembership membership = ownRelationship(originalPort, FACTORY.createOwningMembership()); + membership.getOwnedRelatedElement().add(conjugatedPort); + PortConjugation portConjugation = ownRelationship(conjugatedPort, FACTORY.createPortConjugation()); + ElementUtil.postProcess(portConjugation); + assertSame(originalPort, portConjugation.getOriginalPortDefinition()); + assertSame(conjugatedPort, portConjugation.getConjugatedType()); + + Package importingNamespace = FACTORY.createPackage(); + NamespaceImport namespaceImport = ownRelationship(importingNamespace, FACTORY.createNamespaceImport()); + ElementUtil.postProcess(namespaceImport); + assertSame(importingNamespace, namespaceImport.getImportedNamespace()); + } + + private static Feature addFeatureTarget(Relationship relationship) { + Feature target = FACTORY.createFeature(); + relationship.getOwnedRelatedElement().add(target); + return target; + } + + private static T ownRelationship(org.omg.sysml.lang.sysml.Element owner, T relationship) { + owner.getOwnedRelationship().add(relationship); + return relationship; + } +}