diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index cc66f2aca2..957c4c2fd0 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -52,6 +52,8 @@ This also fixes `relatedFeature`, `sourceFeature` and `targetFeature`, which wer - 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. +- https://github.com/eclipse-syson/syson/issues/2447[#2447] [export] Fix the export of a comment body containing `*/`, which closed the block comment early and produced a file that could not be parsed. +The grammar defines a comment as a non greedy match up to the first `*/`, with no escape sequence and no nesting, so that sequence is exported as `* /` and a warning is reported, since the exported text is then not exactly the one that was written. - https://github.com/eclipse-syson/syson/issues/2340[#2340] [diagrams] Replace the name of the _New Perform action_ tool to _New Perform Action_ to be consistent with other tools. - https://github.com/eclipse-syson/syson/issues/2437[#2437] [metamodel] Fix elementId when creating a FlowDefinition - https://github.com/eclipse-syson/syson/issues/2384[#2384] [import] Fix the import of `TransitionUsage` using _start_ which now set the source to the `StateUsage` _start_ from the standard library instead of `ActionUsage` _start_ from the standard library. 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 d2f15880ae..eb00e27c97 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 @@ -1979,6 +1979,21 @@ public void portUsageFull() { } + @Test + public void documentationContainingACommentTerminator() { + PartUsage partUsage = this.builder.createWithName(PartUsage.class, "PartUsage1"); + this.builder.createIn(Documentation.class, partUsage).setBody("Closing a block comment looks like */ in the code"); + + // The terminator is separated so it no longer closes the comment, which would leave the rest of the body + // exported as if it were SysML v2 code. + this.assertTextualFormEquals(""" + part PartUsage1 { + doc /* Closing a block comment looks like * / in the code */ + }""", partUsage); + assertTrue(this.status.stream() + .anyMatch(reported -> reported.severity() == Severity.WARNING && reported.message().contains("closes a comment"))); + } + @Test public void documentation() { 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 73e8c5a510..a01513524f 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 @@ -170,6 +170,12 @@ public class SysMLElementSerializer extends SysmlSwitch { private static final String SPACE = " "; + /** The sequence closing a block comment, which cannot appear inside a comment body. */ + private static final String COMMENT_TERMINATOR = "*/"; + + /** The form the block comment terminator is exported as, so it no longer closes the comment. */ + private static final String ESCAPED_COMMENT_TERMINATOR = "* /"; + private final String lineSeparator; private final String indentation; @@ -2530,10 +2536,38 @@ private T getDirectContainer(EObject element, java.lang.Class expected) { private String getCommentBody(String body) { Appender subBuilder = this.newAppender(); - subBuilder.append("/* ").append(body).append(" */"); + subBuilder.append("/* ").append(this.escapeCommentTerminator(body)).append(" */"); return subBuilder.toString(); } + /** + * Replace the occurrences of the block comment terminator in a comment body. + *

+ * The grammar defines a comment as {@code /*[\s\S]*?*}{@code /}, a non greedy match up to the first {@code *} + * {@code /}, with no escape sequence and no nesting, so that sequence cannot be represented inside a comment body. + * Left as it is, it closes the comment early and the rest of the body is exported as if it were SysML v2 code, + * giving a file which does not parse. It is separated by a space instead, which is reported since the exported text + * is then not exactly the one which was written. + *

+ *

+ * The reverse replacement is deliberately not done on import: a body may legitimately contain the separated form, + * and turning it back would corrupt it. + *

+ * + * @param body + * the body of a comment, a documentation or a textual representation + * @return the body, with any block comment terminator separated by a space + */ + private String escapeCommentTerminator(String body) { + if (body == null || !body.contains(COMMENT_TERMINATOR)) { + return body; + } + this.reportConsumer.accept(Status.warning( + "A comment body contains \"{0}\", which closes a comment. It has been exported as \"{1}\" so the text remains valid.", + COMMENT_TERMINATOR, ESCAPED_COMMENT_TERMINATOR)); + return body.replace(COMMENT_TERMINATOR, ESCAPED_COMMENT_TERMINATOR); + } + private void appendLocale(Appender builder, String local) { if (!this.isNullOrEmpty(local)) { builder.appendSpaceIfNeeded().append("locale").append(" \"").append(local).append("\""); 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 24d8abeef1..e0b2ace5c6 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 @@ -124,6 +124,10 @@ The affected connections were missing their source and target and were therefore Exporting such a connection and importing it back no longer loses which end is which. ** Fix the import of `TransitionUsage` using _start_ which now set the source to the `StateUsage` _start_ from the standard library instead of `ActionUsage` _start_ from the standard library. +** Fix the export of a documentation, a comment or a textual representation whose text contains `*/`. +That sequence closes a comment in the SysML v2 textual notation, so the exported file could no longer be read. +It is now exported as `* /`, and a warning reports it, since the exported text is then not exactly the one that was written. + == Improvements * {product} Docker images now support `linux/amd64` and `linux/arm64` and run natively on arm64 machines such as Apple Silicon or AWS Graviton.