From aa55f2dd207a4c54dc4b62514ef9220a825b611c Mon Sep 17 00:00:00 2001 From: Florian Kleedorfer Date: Wed, 30 Oct 2024 02:46:22 +0100 Subject: [PATCH] Fix numeric literal formatting Prior to this commit, in some situations, formatting changes the datatype of numeric literals (xsd:double and xsd:decimal). The change happens if the lexical form is ambiguous because it does not include a fractional part. This commit checks for this condition and uses the quoted form in these cases. --- .../turtle/formatter/TurtleFormatter.java | 12 ++- .../turtle/formatter/TurtleFormatterTest.java | 73 +++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/atextor/turtle/formatter/TurtleFormatter.java b/src/main/java/de/atextor/turtle/formatter/TurtleFormatter.java index 5fcc3ef..2a2196b 100644 --- a/src/main/java/de/atextor/turtle/formatter/TurtleFormatter.java +++ b/src/main/java/de/atextor/turtle/formatter/TurtleFormatter.java @@ -54,6 +54,10 @@ public class TurtleFormatter implements Function, BiConsumerEscape Sequences. *

@@ -626,7 +630,8 @@ private State writeLiteral( final Literal literal, final State state ) { if ( datatypeUri.equals( XSD.xdouble.getURI() ) ) { if ( style.enableDoubleFormatting ) { return state.write( style.doubleFormat.format( literal.getDouble() ) ); - } else { + } else if (XSD_DOUBLE_UNQUOTED_REGEX.matcher(literal.getLexicalForm()).matches()) { + // only use unquoted form if it will be parsed as an xsd:double return state.write( literal.getLexicalForm() ); } } @@ -637,7 +642,10 @@ private State writeLiteral( final Literal literal, final State state ) { return state.write( quoteAndEscape( literal ) ); } if ( datatypeUri.equals( XSD.decimal.getURI() ) ) { - return state.write( literal.getLexicalForm() ); + if (XSD_DECIMAL_UNQUOTED_REGEX.matcher(literal.getLexicalForm()).matches()) { + // only use unquoted form if it will be parsed as an xsd:decimal + return state.write(literal.getLexicalForm()); + } } if ( datatypeUri.equals( XSD.integer.getURI() ) ) { return state.write( literal.getLexicalForm() ); diff --git a/src/test/java/de/atextor/turtle/formatter/TurtleFormatterTest.java b/src/test/java/de/atextor/turtle/formatter/TurtleFormatterTest.java index ea50f15..5d90679 100644 --- a/src/test/java/de/atextor/turtle/formatter/TurtleFormatterTest.java +++ b/src/test/java/de/atextor/turtle/formatter/TurtleFormatterTest.java @@ -1084,6 +1084,79 @@ public void testIntegerLiteralWithLeadingZeros(){ assertThat(result.trim()).isEqualTo(expected); } + @Test + public void testDoubleLiteralWithoutFractions(){ + String content = """ + @prefix xsd: . + @prefix : . + :thing :value "40"^^xsd:double. + """; + String expected = """ + @prefix xsd: . + @prefix : . + + :thing :value "40"^^xsd:double ."""; + 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 testDoubleLiteralWithFractions(){ + String content = """ + @prefix xsd: . + @prefix : . + :thing :value "4.001E2"^^xsd:double. + """; + String expected = """ + @prefix xsd: . + @prefix : . + + :thing :value 4.001E2 ."""; + 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 testDecimalLiteralWithoutFractions(){ + String content = """ + @prefix xsd: . + @prefix : . + :thing :value "40"^^xsd:decimal. + """; + String expected = """ + @prefix xsd: . + @prefix : . + + :thing :value "40"^^xsd:decimal ."""; + 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 testDecimalLiteralWithFractions(){ + String content = """ + @prefix xsd: . + @prefix : . + :thing :value "40.0001"^^xsd:decimal. + """; + String expected = """ + @prefix xsd: . + @prefix : . + + :thing :value 40.0001 ."""; + 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(); final InputStream stream = new ByteArrayInputStream( content.getBytes( StandardCharsets.UTF_8 ) );