From 464a6769db3d8e067353d66a29e5091fab59e7b5 Mon Sep 17 00:00:00 2001 From: kkkk1258999 Date: Thu, 13 Aug 2026 20:10:13 +0900 Subject: [PATCH] [2419] Export the metadata of the ends of a connector declared inline The prefix metadata carried by the ends of a connector were dropped by the export when those ends are owned through an EndFeatureMembership, which is the shape produced by every connector created from a diagram. appendConnectorEnd wrote the multiplicity, the declared name and the reference subsetting of an end, but not its prefix metadata, although appendExtensionKeyword is already used for that purpose elsewhere, on the connector itself for instance. Calling it on the end as well restores them. This went unnoticed because a connector whose ends are owned through a plain FeatureMembership, which is what the textual import produces, does not go through appendConnectorEnd at all: appendConnectorPart finds no EndFeatureMembership, the ends are left out of childrenMembershipToSkip, and they end up serialized as regular children, metadata included. Only the inline form lost them. Concretely, a requirement derivation created from a diagram exported as losing the #original and #derive of its ends, so which requirement is the original one and which one is derived was no longer stated in the exported text. Bug: https://github.com/eclipse-syson/syson/issues/2419 Signed-off-by: kkkk1258999 --- CHANGELOG.adoc | 2 + .../textual/SysMLElementSerializerTest.java | 45 +++++++++++++++++++ .../textual/SysMLElementSerializer.java | 3 ++ .../pages/release-notes/2026.9.0.adoc | 5 +++ 4 files changed, 55 insertions(+) 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: