From 92e9fe94f8ffd70e63edd81894f37cad29d36bfa Mon Sep 17 00:00:00 2001 From: Florian Kleedorfer Date: Wed, 9 Jul 2025 12:18:48 +0200 Subject: [PATCH] Handle platform specific line terminators gracefully in multiline strings --- README.md | 2 + .../turtle/formatter/TurtleFormatter.java | 8 +- .../turtle/formatter/TurtleFormatterTest.java | 92 +++++++++++++++++++ 3 files changed, 99 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d418756..ff4856a 100644 --- a/README.md +++ b/README.md @@ -708,6 +708,8 @@ elements in RDF lists. \* Adapted from [EditorConfig](https://editorconfig.org/#file-format-details) ## Release Notes +* Unreleased + * Replace Platform-dependent line terminators in multiline strings with unix style newlines (`\n`, `\r`, and `\r\n` -> `\n`) * 1.2.16: * Bugfix: Empty RDF lists are formatted as empty set of parenthesis again * 1.2.15: diff --git a/src/main/java/de/atextor/turtle/formatter/TurtleFormatter.java b/src/main/java/de/atextor/turtle/formatter/TurtleFormatter.java index 78f5354..eec7229 100644 --- a/src/main/java/de/atextor/turtle/formatter/TurtleFormatter.java +++ b/src/main/java/de/atextor/turtle/formatter/TurtleFormatter.java @@ -66,7 +66,7 @@ public class TurtleFormatter implements Function, BiConsumer */ - private static final Pattern STRING_ESCAPE_SEQUENCES = Pattern.compile( "[\t\b\n\r\f\"\\\\]" ); + private static final Pattern STRING_ESCAPE_SEQUENCES = Pattern.compile( "(\r\n)|[\t\b\n\r\f\"\\\\]" ); private final FormattingStyle style; @@ -680,13 +680,15 @@ private String quoteAndEscape( final RDFNode node ) { final String quote = switch ( style.quoteStyle ) { case ALWAYS_SINGE_QUOTES -> "\""; case ALWAYS_TRIPLE_QUOTES -> "\"\"\""; - case TRIPLE_QUOTES_FOR_MULTILINE -> value.contains( "\n" ) ? "\"\"\"" : "\""; + case TRIPLE_QUOTES_FOR_MULTILINE -> (value.contains( "\n" ) || value.contains("\r"))? "\"\"\"" : "\""; }; + final Map characterReplacements = Map.of( "\t", "\\\\t", "\b", "\\\\b", - "\r", "\\\\r", + "\r", quote.equals( "\"" ) ? "\\\\r": "\n", // in multiline strings that were read with mac style endings, replace \r with \n + "\r\n", quote.equals( "\"" ) ? "\\\\r\\\\n": "\n", // in multiline strings that were read with windows style endings, replace \r\n with \n "\f", "\\\\f", "\n", quote.equals( "\"" ) ? "\\\\n" : "\n", // Don't escape line breaks in triple-quoted strings "\"", quote.equals( "\"" ) ? "\\\\\"" : "\"", // Don't escape quotes in triple-quoted strings diff --git a/src/test/java/de/atextor/turtle/formatter/TurtleFormatterTest.java b/src/test/java/de/atextor/turtle/formatter/TurtleFormatterTest.java index b76791d..1f02193 100644 --- a/src/test/java/de/atextor/turtle/formatter/TurtleFormatterTest.java +++ b/src/test/java/de/atextor/turtle/formatter/TurtleFormatterTest.java @@ -1230,6 +1230,98 @@ public void testEmptyList() { assertThat(result.trim()).isEqualTo(expected); } + @Test + public void testMultilineStrings(){ + String content = """ + @prefix xsd: . + @prefix : . + :thing :value \""" + First Line + Second Line + Third Line + \""" . + """; + String expected = """ + @prefix xsd: . + @prefix : . + + :thing :value \""" + First Line + Second Line + Third Line + \""" ."""; + final FormattingStyle style = FormattingStyle.DEFAULT; + final TurtleFormatter formatter = new TurtleFormatter(style); + final String result = formatter.applyToContent(content); + assertThat(result.trim()).isEqualTo(expected); + } + + @Test + public void testStringsWithUnixStyleNewlinesToMultilineStrings(){ + String content = """ + @prefix xsd: . + @prefix : . + :thing :value "\\n First Line\\n Second Line\\n Third Line\\n" . + """; + String expected = """ + @prefix xsd: . + @prefix : . + + :thing :value \""" + First Line + Second Line + Third Line + \""" ."""; + final FormattingStyle style = FormattingStyle.DEFAULT; + final TurtleFormatter formatter = new TurtleFormatter(style); + final String result = formatter.applyToContent(content); + assertThat(result.trim()).isEqualTo(expected); + } + + @Test + public void testStringsWithWindowsStyleNewlinesToMultilineStrings(){ + String content = """ + @prefix xsd: . + @prefix : . + :thing :value "\\r\\n First Line\\r\\n Second Line\\r\\n Third Line\\r\\n" . + """; + String expected = """ + @prefix xsd: . + @prefix : . + + :thing :value \""" + First Line + Second Line + Third Line + \""" ."""; + final FormattingStyle style = FormattingStyle.DEFAULT; + final TurtleFormatter formatter = new TurtleFormatter(style); + final String result = formatter.applyToContent(content); + assertThat(result.trim()).isEqualTo(expected); + } + + @Test + public void testStringsWithMacStyleNewlinesToMultilineStrings(){ + String content = """ + @prefix xsd: . + @prefix : . + :thing :value "\\r First Line\\r Second Line\\r Third Line\\r" . + """; + String expected = """ + @prefix xsd: . + @prefix : . + + :thing :value \""" + First Line + Second Line + Third Line + \""" ."""; + final FormattingStyle style = FormattingStyle.DEFAULT; + final TurtleFormatter formatter = new TurtleFormatter(style); + final String result = formatter.applyToContent(content); + assertThat(result.trim()).isEqualTo(expected); + } + private Model modelFromString( final String content ) { final Model model = ModelFactory.createDefaultModel();