diff --git a/org.omg.sysml.interactive.tests/src/org/omg/sysml/interactive/tests/MultiplicityPreparationTest.java b/org.omg.sysml.interactive.tests/src/org/omg/sysml/interactive/tests/MultiplicityPreparationTest.java new file mode 100644 index 0000000000..6452238e8e --- /dev/null +++ b/org.omg.sysml.interactive.tests/src/org/omg/sysml/interactive/tests/MultiplicityPreparationTest.java @@ -0,0 +1,255 @@ +/******************************************************************************* + * SysML 2 Pilot Implementation + * Copyright (c) 2026 Obeo + * Copyright (c) 2026 Model Driven Solutions, Inc. + * + * 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.interactive.tests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.junit.After; +import org.junit.BeforeClass; +import org.junit.Test; +import org.omg.sysml.interactive.SysMLInteractive; +import org.omg.sysml.interactive.SysMLInteractiveResult; +import org.omg.sysml.lang.sysml.Element; +import org.omg.sysml.lang.sysml.Multiplicity; +import org.omg.sysml.lang.sysml.MultiplicityRange; +import org.omg.sysml.lang.sysml.OwningMembership; +import org.omg.sysml.lang.sysml.Relationship; +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.Usage; +import org.omg.sysml.util.ElementUtil; +import org.omg.sysml.util.TypeUtil; +import org.omg.sysml.xtext.postprocessing.SysMLParserPostProcessorFactory; + +/** Tests the boundary between parser preparation of multiplicities and derived consultation. */ +public class MultiplicityPreparationTest { + + protected static SysMLInteractive interactive; + + /** Loads and resolves the standard library once for the textual test scenarios. */ + @BeforeClass + public static void loadLibraries() { + interactive = SysMLInteractive.createInstance(); + interactive.setVerbose(false); + interactive.loadLibrary(Path.of(System.getProperty("libraryPath")).toAbsolutePath().toString()); + } + + /** Removes each test model while retaining the shared resolved libraries. */ + @After + public void removeResource() { + interactive.removeResource(); + } + + /** + * Parsing end usages prepares one multiplicity before any feature consultation; + * subsequent reads must preserve that multiplicity and its owning relationships. + */ + @Test + public void parserPreparesEndMultiplicitiesBeforeConsultation() throws Exception { + // Each end has an implicit multiplicity, supplied by parser post-processing. + Resource resource = parse("endMultiplicities.sysml", """ + part def Holder { + end attribute attributeEnd; + end item itemEnd; + end port portEnd; + end ref referenceEnd; + end connection connectionEnd; + } + """); + for (String name : List.of("attributeEnd", "itemEnd", "portEnd", "referenceEnd", "connectionEnd")) { + Usage usage = findByName(resource, name, Usage.class); + List multiplicities = TypeUtil.getOwnedMultiplicitiesOf(usage); + assertEquals(name, 1, multiplicities.size()); + List relationships = new ArrayList<>(usage.getOwnedRelationship()); + + usage.getFeature(); + usage.getFeature(); + assertEquals(name, relationships, usage.getOwnedRelationship()); + assertSame(name, multiplicities.get(0), TypeUtil.getOwnedMultiplicitiesOf(usage).get(0)); + } + } + + /** + * Explicit multiplicities are retained by repeated parser preparation, without + * creating a second multiplicity or replacing the existing model element. + */ + @Test + public void repeatedPreparationPreservesExplicitMultiplicity() throws Exception { + // The explicit range must remain the sole owned multiplicity. + Resource resource = parse("explicitMultiplicity.sysml", """ + part def Holder { end item endpoint[2..4]; } + """); + Usage usage = findByName(resource, "endpoint", Usage.class); + assertEquals(1, TypeUtil.getOwnedMultiplicitiesOf(usage).size()); + Multiplicity multiplicity = TypeUtil.getOwnedMultiplicitiesOf(usage).get(0); + assertTrue(multiplicity instanceof MultiplicityRange); + List relationships = new ArrayList<>(usage.getOwnedRelationship()); + + // Re-entering preparation must not duplicate or replace the explicit range. + SysMLParserPostProcessorFactory.getPostProcessor(usage).postProcess(); + SysMLParserPostProcessorFactory.getPostProcessor(usage).postProcess(); + assertEquals(relationships, usage.getOwnedRelationship()); + assertEquals(1, TypeUtil.getOwnedMultiplicitiesOf(usage).size()); + assertSame(multiplicity, TypeUtil.getOwnedMultiplicitiesOf(usage).get(0)); + } + + /** + * Removing a prepared multiplicity simulates an edited model without that owned + * element; feature consultation must not recreate it, even after clearing caches. + */ + @Test + public void consultationDoesNotRecreateRemovedMultiplicity() throws Exception { + Resource resource = parse("removedMultiplicity.sysml", """ + part def Holder { end item endpoint; } + """); + Usage usage = findByName(resource, "endpoint", Usage.class); + assertEquals(1, TypeUtil.getOwnedMultiplicitiesOf(usage).size()); + + // This intermediate edited state cannot be obtained by parsing alone: remove + // the multiplicity created by the parser, while retaining the end usage. + usage.getOwnedRelationship().removeIf(relationship -> relationship instanceof OwningMembership + && ((OwningMembership) relationship).getOwnedMemberElement() instanceof Multiplicity); + ElementUtil.clearCachesOf(usage); + List relationships = new ArrayList<>(usage.getOwnedRelationship()); + + // A cold feature query must leave zero owned multiplicities, not materialize one. + usage.getFeature(); + usage.getFeatureMembership(); + assertTrue(TypeUtil.getOwnedMultiplicitiesOf(usage).isEmpty()); + assertEquals(relationships, usage.getOwnedRelationship()); + } + + /** + * Eligible type-owned usages receive a plain default multiplicity even with + * explicit subsettings or redefinitions, including chain and package targets. + */ + @Test + public void explicitSpecializationsReceiveDefaultMultiplicity() throws Exception { + Resource resource = parse("specializedMultiplicities.sysml", """ + attribute globalValues[0..*]; + part def Base { attribute values[0..*]; } + part def Derived :> Base { + attribute redefinedValues redefines values; + attribute subsetValues subsets values; + part nested : Base; + attribute chainValues subsets nested.values; + attribute packageValues subsets globalValues; + attribute plainValues; + } + """); + for (String name : List.of("redefinedValues", "subsetValues", "chainValues", "packageValues")) { + Usage usage = findByName(resource, name, Usage.class); + // ST6RI-774 allows the default even when the target has [0..*]. + assertEquals(name, 1, TypeUtil.getOwnedMultiplicitiesOf(usage).size()); + Multiplicity multiplicity = TypeUtil.getOwnedMultiplicitiesOf(usage).get(0); + assertEquals(SysMLPackage.Literals.MULTIPLICITY, multiplicity.eClass()); + usage.getFeature(); + SysMLParserPostProcessorFactory.getPostProcessor(usage).postProcess(); + assertEquals(name, 1, TypeUtil.getOwnedMultiplicitiesOf(usage).size()); + } + assertEquals(1, TypeUtil.getOwnedMultiplicitiesOf(findByName(resource, "plainValues", Usage.class)).size()); + + // Explicit transformation must retain the same multiplicity choices. + ElementUtil.transformAll(resource, true); + for (String name : List.of("redefinedValues", "subsetValues", "chainValues", "packageValues")) { + assertEquals(name, 1, TypeUtil.getOwnedMultiplicitiesOf(findByName(resource, name, Usage.class)).size()); + } + } + + /** + * Cyclic explicit subsettings still receive default multiplicities; repeated parser + * preparation must terminate without resolving either target. + */ + @Test + public void cyclicSubsettingsDoNotResolveTargets() throws Exception { + Resource resource = parse("cyclicMultiplicities.sysml", """ + part def Holder { + attribute left subsets right; + attribute right subsets left; + } + """); + for (String name : List.of("left", "right")) { + Usage usage = findByName(resource, name, Usage.class); + Subsetting subsetting = usage.getOwnedRelationship().stream() + .filter(Subsetting.class::isInstance).map(Subsetting.class::cast).findFirst().orElseThrow(); + // Non-resolving reads verify that multiplicity preparation leaves the proxy intact. + Object target = subsetting.eGet(SysMLPackage.Literals.SUBSETTING__SUBSETTED_FEATURE, false); + assertTrue(target instanceof EObject); + assertTrue(((EObject) target).eIsProxy()); + SysMLParserPostProcessorFactory.getPostProcessor(usage).postProcess(); + assertSame(target, subsetting.eGet(SysMLPackage.Literals.SUBSETTING__SUBSETTED_FEATURE, false)); + assertTrue(((EObject) target).eIsProxy()); + assertEquals(name, 1, TypeUtil.getOwnedMultiplicitiesOf(usage).size()); + } + } + + /** + * Only eligible type-owned usages receive a default; package-owned usages, + * ordinary references and non-end connections retain no owned multiplicity. + */ + @Test + public void defaultMultiplicityDependsOnKindAndOwner() throws Exception { + Resource resource = parse("multiplicityKinds.sysml", """ + attribute packageAttribute; + item packageItem; + part packagePart; + port packagePort; + part def Holder { + attribute ownedAttribute; + item ownedItem; + part ownedPart; + port ownedPort; + ref reference; + connection link; + } + """); + // Eligible usages receive Multiplicity, never a synthetic MultiplicityRange. + for (String name : List.of("ownedAttribute", "ownedItem", "ownedPart", "ownedPort")) { + List multiplicities = TypeUtil.getOwnedMultiplicitiesOf(findByName(resource, name, Usage.class)); + assertEquals(name, 1, multiplicities.size()); + assertEquals(name, SysMLPackage.Literals.MULTIPLICITY, multiplicities.get(0).eClass()); + } + // Neither package ownership nor an ineligible kind may introduce a default. + for (String name : List.of("packageAttribute", "packageItem", "packagePart", "packagePort", "reference", "link")) { + assertTrue(name, TypeUtil.getOwnedMultiplicitiesOf(findByName(resource, name, Usage.class)).isEmpty()); + } + } + + private Resource parse(String name, String text) throws Exception { + interactive.setResource(name); + interactive.parse(text); + Element rootElement = interactive.getRootElement(); + SysMLInteractiveResult result = new SysMLInteractiveResult(rootElement, Collections.emptyList()); + assertTrue("'" + text + "':\n" + result.formatIssues(), result.getIssues().isEmpty()); + return interactive.getResource(); + } + + private static T findByName(Resource resource, String name, Class kind) { + for (var contents = resource.getAllContents(); contents.hasNext();) { + EObject object = contents.next(); + if (kind.isInstance(object) && name.equals(kind.cast(object).getDeclaredName())) { + return kind.cast(object); + } + } + throw new AssertionError("Missing " + name); + } + +} diff --git a/org.omg.sysml.interactive/src/org/omg/sysml/interactive/SysMLInteractive.java b/org.omg.sysml.interactive/src/org/omg/sysml/interactive/SysMLInteractive.java index b5f00764af..4fb4e857a7 100644 --- a/org.omg.sysml.interactive/src/org/omg/sysml/interactive/SysMLInteractive.java +++ b/org.omg.sysml.interactive/src/org/omg/sysml/interactive/SysMLInteractive.java @@ -164,11 +164,15 @@ public String getApiBasePath() { } public int next(String extension) { - this.resource = this.createResource(counter + extension); + this.setResource(counter + extension); this.addInputResource(this.resource); return this.counter++; } + public void setResource(String path) { + this.resource = this.createResource(path); + } + public Resource getResource() { return this.resource; } diff --git a/org.omg.sysml.logic.bundle/lib/org.omg.sysml.logic-0.63.0-SNAPSHOT.jar b/org.omg.sysml.logic.bundle/lib/org.omg.sysml.logic-0.63.0-SNAPSHOT.jar index 80930d5474..9f62f5b33a 100644 Binary files a/org.omg.sysml.logic.bundle/lib/org.omg.sysml.logic-0.63.0-SNAPSHOT.jar and b/org.omg.sysml.logic.bundle/lib/org.omg.sysml.logic-0.63.0-SNAPSHOT.jar differ diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/AttributeUsageAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/AttributeUsageAdapter.java index 0ca7f548e1..0c68e8fccf 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/AttributeUsageAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/AttributeUsageAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2021 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 @@ -31,10 +32,4 @@ public AttributeUsageAdapter(AttributeUsage element) { public AttributeUsage getTarget() { return (AttributeUsage)super.getTarget(); } - - @Override - protected boolean isAddMultiplicity() { - return isAddDefaultMultiplicity(); - } - } diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ConnectionUsageAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ConnectionUsageAdapter.java index 110475b705..81af91566f 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ConnectionUsageAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ConnectionUsageAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2021, 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 @@ -61,12 +62,7 @@ protected String getDefaultSupertype() { getDefaultSupertype("base"): getDefaultSupertype("binary"); } - - @Override - protected boolean isAddMultiplicity() { - return getTarget().isEnd(); - } - + @Override public void doTransform() { ConnectionUsage target = getTarget(); diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ItemUsageAdapter.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ItemUsageAdapter.java index 3fd9841d58..da52686c78 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ItemUsageAdapter.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/adapter/ItemUsageAdapter.java @@ -1,6 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation * Copyright (c) 2021, 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 @@ -65,12 +66,4 @@ public boolean isSubitem() { return target.isComposite() && (owningType instanceof ItemDefinition || owningType instanceof ItemUsage); } - - // Transformation - - @Override - protected boolean isAddMultiplicity() { - return isAddDefaultMultiplicity(); - } - } 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 c10dad2f0d..27e57a1006 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 @@ -76,12 +76,4 @@ public boolean isSubport() { Type owningType = target.getOwningType(); return target.isComposite() && (owningType instanceof PortDefinition || owningType instanceof PortUsage); } - - // Transformation - - @Override - protected boolean isAddMultiplicity() { - return isAddDefaultMultiplicity(); - } - } 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 a263c5a4df..edef5c8ed2 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,11 +1,7 @@ /******************************************************************************* * SysML 2 Pilot Implementation -<<<<<<< HEAD - * Copyright (c) 2021-2025, 2026 Model Driven Solutions, Inc. - * Copyright (c) 2026 Obeo -======= * Copyright (c) 2021-2026 Model Driven Solutions, Inc. ->>>>>>> refs/remotes/origin/HEAD + * 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 @@ -145,30 +141,6 @@ public void addDefaultGeneralType() { // Transformation - // Used to check for default multiplicity for AttributeUsages, ItemUsages and PortUsages. - protected boolean isAddDefaultMultiplicity() { - Usage target = getTarget(); - return target.isEnd() || - target.getOwningType() != null && - target.getOwnedSubsetting().stream(). - map(Subsetting::getSubsettedFeature). - filter(f->f != null). - map(FeatureUtil::getBasicFeatureOf). - noneMatch(f->f != null && f.getOwningType() != null); - } - - // Multiplicity of 1..1 is always the default for an end usage. - protected boolean isAddMultiplicity() { - return getTarget().isEnd(); - } - - @Override - public void addAdditionalMembers() { - if (isAddMultiplicity()) { - TypeUtil.addMultiplicityTo(getTarget()); - } - } - /** * Return the relevant subject parameter to which a Usage should be bound. */ diff --git a/org.omg.sysml.logic/src/main/java/org/omg/sysml/util/TypeUtil.java b/org.omg.sysml.logic/src/main/java/org/omg/sysml/util/TypeUtil.java index ee045eb96e..85a7df4220 100644 --- a/org.omg.sysml.logic/src/main/java/org/omg/sysml/util/TypeUtil.java +++ b/org.omg.sysml.logic/src/main/java/org/omg/sysml/util/TypeUtil.java @@ -540,6 +540,12 @@ public static void addMultiplicityTo(Type type) { } } + public static List getOwnedMultiplicitiesOf(Type type) { + return NamespaceUtil.getOwnedMembersOf(type). + filter(Multiplicity.class::isInstance). + map(Multiplicity.class::cast).toList(); + } + public static Multiplicity getMultiplicityOf(Type type) { List multiplicities = getMultiplicitiesOf(type, new HashSet<>()); return multiplicities.isEmpty()? null: multiplicities.get(0); diff --git a/org.omg.sysml.model.bundle/lib/org.omg.sysml.model-0.63.0-SNAPSHOT.jar b/org.omg.sysml.model.bundle/lib/org.omg.sysml.model-0.63.0-SNAPSHOT.jar index 935a6900b7..14d2eb57e4 100644 Binary files a/org.omg.sysml.model.bundle/lib/org.omg.sysml.model-0.63.0-SNAPSHOT.jar and b/org.omg.sysml.model.bundle/lib/org.omg.sysml.model-0.63.0-SNAPSHOT.jar differ diff --git a/org.omg.sysml.xpect.tests/src/org/omg/sysml/xpect/tests/validation/invalid/DefaultMultiplicity_invalid.sysml.xt b/org.omg.sysml.xpect.tests/src/org/omg/sysml/xpect/tests/validation/invalid/DefaultMultiplicity_invalid.sysml.xt index b21124560a..c2bff11207 100644 --- a/org.omg.sysml.xpect.tests/src/org/omg/sysml/xpect/tests/validation/invalid/DefaultMultiplicity_invalid.sysml.xt +++ b/org.omg.sysml.xpect.tests/src/org/omg/sysml/xpect/tests/validation/invalid/DefaultMultiplicity_invalid.sysml.xt @@ -48,7 +48,7 @@ package DefaultMultiplicity { port t1; part def P { - // These kinds of usages have default multiplicity of [1..1]. + // ST6RI-774: explicit subsettings do not exclude the default [1..1]. attribute a2 :> a1; item i2 :> i1; part p2 :> p1; @@ -63,6 +63,7 @@ package DefaultMultiplicity { // XPECT warnings ---> "Subsetting/redefining feature should not have larger multiplicity upper bound" at "t2" port t3[*] :> t2; + // Without explicit subsettings, the default [1..1] still applies. attribute a4; item i4; part p4; diff --git a/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/AttributeUsageParserPostProcessor.java b/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/AttributeUsageParserPostProcessor.java new file mode 100644 index 0000000000..c5aaf607f4 --- /dev/null +++ b/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/AttributeUsageParserPostProcessor.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.xtext.postprocessing; + +import org.omg.sysml.lang.sysml.AttributeUsage; +import org.omg.sysml.lang.sysml.Usage; + +public class AttributeUsageParserPostProcessor extends UsageParserPostProcessor { + + public AttributeUsageParserPostProcessor(Usage element) { + super(element); + } + + @Override + public AttributeUsage getTarget() { + return (AttributeUsage)super.getTarget(); + } + + @Override + protected boolean isAddMultiplicity() { + return isAddDefaultMultiplicity(); + } +} diff --git a/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/ConnectionUsageParserPostProcessor.java b/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/ConnectionUsageParserPostProcessor.java index 5ffd905e90..e8370c75ce 100644 --- a/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/ConnectionUsageParserPostProcessor.java +++ b/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/ConnectionUsageParserPostProcessor.java @@ -22,4 +22,9 @@ public ConnectionUsageParserPostProcessor(ConnectionUsage element) { public ConnectionUsage getTarget() { return (ConnectionUsage)super.getTarget(); } + + @Override + protected boolean isAddMultiplicity() { + return getTarget().isEnd(); + } } diff --git a/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/ItemUsageParserPostProcessor.java b/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/ItemUsageParserPostProcessor.java index 6a2b032ac5..e708c5ee71 100644 --- a/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/ItemUsageParserPostProcessor.java +++ b/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/ItemUsageParserPostProcessor.java @@ -22,4 +22,9 @@ public ItemUsageParserPostProcessor(ItemUsage element) { public ItemUsage getTarget() { return (ItemUsage)super.getTarget(); } + + @Override + protected boolean isAddMultiplicity() { + return isAddDefaultMultiplicity(); + } } diff --git a/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/PortUsageParserPostProcessor.java b/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/PortUsageParserPostProcessor.java index 395161e15f..3f329ef823 100644 --- a/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/PortUsageParserPostProcessor.java +++ b/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/PortUsageParserPostProcessor.java @@ -36,4 +36,9 @@ public void postProcess() { target.setIsComposite(false); } } + + @Override + protected boolean isAddMultiplicity() { + return isAddDefaultMultiplicity(); + } } diff --git a/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/SysMLParserPostProcessorFactory.java b/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/SysMLParserPostProcessorFactory.java index 40250b1c27..91488691dc 100644 --- a/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/SysMLParserPostProcessorFactory.java +++ b/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/SysMLParserPostProcessorFactory.java @@ -84,7 +84,9 @@ public ElementParserPostProcessor caseUsage(Usage element) { return new UsageParserPostProcessor(element); } + @Override + public ElementParserPostProcessor caseAttributeUsage(AttributeUsage element) { + return new AttributeUsageParserPostProcessor(element); + } } - - } diff --git a/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/UsageParserPostProcessor.java b/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/UsageParserPostProcessor.java index 9616c6c72c..eb64fc35d8 100644 --- a/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/UsageParserPostProcessor.java +++ b/org.omg.sysml.xtext/src/org/omg/sysml/xtext/postprocessing/UsageParserPostProcessor.java @@ -21,6 +21,7 @@ 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.TypeUtil; import org.omg.sysml.util.UsageUtil; public class UsageParserPostProcessor extends FeatureParserPostProcessor { @@ -57,6 +58,9 @@ public void postProcess () { featuringType instanceof AttributeDefinition || featuringType instanceof AttributeUsage) { target.setIsComposite(false); } + if (isAddMultiplicity()) { + TypeUtil.addMultiplicityTo(getTarget()); + } } private void setIsCompositeIfUnset(Usage target) { @@ -76,4 +80,19 @@ private static boolean isCompositeByDefault(EClass eClass) { @Override protected void setIsVariableIfConstant() { } + + /** + * Checks the owning type for eligible attribute, item and port usages. + * Subsettings and redefinitions do not restrict this default (ST6RI-774); + * their targets are not consulted. End usages always receive the default. + */ + protected boolean isAddDefaultMultiplicity() { + Usage target = getTarget(); + return target.isEnd() || target.getOwningType() != null; + } + + // Multiplicity of 1..1 is always the default for an end usage. + protected boolean isAddMultiplicity() { + return getTarget().isEnd(); + } }