diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 82ed3590f9..fce5adb795 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -47,6 +47,8 @@ It is now _Become nested Concern_ instead of _Become nested Requirement_. Following KerML, where `endFeature` is defined as the owned features having `isEnd = true`, the ends owned through a plain `FeatureMembership` are now collected as well, and not only those owned through an `EndFeatureMembership`. This also fixes `relatedFeature`, `sourceFeature` and `targetFeature`, which were all empty for such connections. - https://github.com/eclipse-syson/syson/issues/2226[#2226] [import] Fix textual import of annotations on relationships so their `annotatedElement` is set and they are returned by `Element#getOwnedAnnotation()`. +- https://github.com/eclipse-syson/syson/issues/2419[#2419] [export] Fix the export of the prefix metadata carried by the ends of a connector, which were dropped when those ends are declared inline. +The ends of a connector created from a diagram are owned through an `EndFeatureMembership` and are exported inline, so their metadata, such as the `#original` and `#derive` of a requirement derivation, were lost. === Improvements diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/sysml/textual/SysMLElementSerializerTest.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/sysml/textual/SysMLElementSerializerTest.java index b05b5e9933..d2f15880ae 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/sysml/textual/SysMLElementSerializerTest.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/sysml/textual/SysMLElementSerializerTest.java @@ -1680,6 +1680,51 @@ public void interfaceUsageWithNestedPortEnd() { this.assertTextualFormEquals("connection interface1 connect livingRoom.heater.socket to outlet;", interfaceUsage); } + @Test + public void connectionUsageWithMetadataOnItsEnds() { + PartUsage rootPart = this.builder.createWithName(PartUsage.class, "owner1"); + + PortUsage socket = this.builder.createInWithName(PortUsage.class, rootPart, "socket"); + PortUsage outlet = this.builder.createInWithName(PortUsage.class, rootPart, "outlet"); + + InterfaceUsage interfaceUsage = this.builder.createWithName(InterfaceUsage.class, "interface1"); + this.addAsFeatureMember(rootPart, interfaceUsage); + + EndFeatureMembership sourceEnd = this.createConnectionEndFeatureMembership(socket); + this.applyMetadataOnEnd(sourceEnd, "original"); + EndFeatureMembership targetEnd = this.createConnectionEndFeatureMembership(outlet); + this.applyMetadataOnEnd(targetEnd, "derive"); + interfaceUsage.getOwnedRelationship().add(sourceEnd); + interfaceUsage.getOwnedRelationship().add(targetEnd); + + this.assertTextualFormEquals("connection interface1 connect #original socket to #derive outlet;", interfaceUsage); + } + + /** + * Applies a prefix metadata on the end owned by the given membership, the way {@code #original} and {@code #derive} + * are applied on the ends of a requirement derivation. + */ + private void applyMetadataOnEnd(EndFeatureMembership endFeatureMembership, String metadataDefinitionName) { + MetadataDefinition metadataDefinition = this.fact.createMetadataDefinition(); + metadataDefinition.setDeclaredName(metadataDefinitionName); + + MetadataUsage metadataUsage = this.fact.createMetadataUsage(); + OwningMembership owningMembership = this.fact.createOwningMembership(); + owningMembership.getOwnedRelatedElement().add(metadataUsage); + owningMembership.setMemberElement(metadataUsage); + + FeatureTyping featureTyping = this.fact.createFeatureTyping(); + featureTyping.setType(metadataDefinition); + featureTyping.setTypedFeature(metadataUsage); + metadataUsage.getOwnedRelationship().add(featureTyping); + + endFeatureMembership.getOwnedRelatedElement().stream() + .filter(Feature.class::isInstance) + .map(Feature.class::cast) + .findFirst() + .ifPresent(endFeature -> endFeature.getOwnedRelationship().add(owningMembership)); + } + @Test public void interfaceUsageWithUnresolvedPortEnd() { PartUsage rootPart = this.builder.createWithName(PartUsage.class, "part1"); diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/SysMLElementSerializer.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/SysMLElementSerializer.java index 952603535d..73e8c5a510 100644 --- a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/SysMLElementSerializer.java +++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/textual/SysMLElementSerializer.java @@ -1743,6 +1743,9 @@ private void appendConnectorEndMember(Appender builder, EndFeatureMembership end } private void appendConnectorEnd(Appender builder, Feature endFeature) { + // Prefix metadata of the end, such as the #original and #derive of a requirement derivation. Without this the + // metadata of an end owned through an EndFeatureMembership would be dropped by the export. + this.appendExtensionKeyword(builder, endFeature); // Handle ownedRelationship += OwnedMultiplicity endFeature.getOwnedRelationship().stream().filter(OwningMembership.class::isInstance) diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc index 315a06d3ec..e1184e5818 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc @@ -92,6 +92,11 @@ Those connections were missing their source and their target, and were therefore ** Fix expression creation and editing on `SuccessionAsUsage` elements. ** Fix the _Typed by_ property in the _Details_ view so a `Usage` can only be typed by its corresponding `Definition` kind. +* In textual import/export: + +** Fix the export of the keywords carried by the ends of a connection, such as the `#original` and `#derive` of a requirement derivation, which were missing from the exported text when the connection had been created from a diagram. +Exporting such a connection and importing it back no longer loses which end is which. + == Improvements * In diagrams: