From 9769788d93f47772c96a0fa4f18b565820df80b9 Mon Sep 17 00:00:00 2001 From: Pierre Maillot Date: Mon, 8 Dec 2025 17:09:59 +0100 Subject: [PATCH 01/12] Adding an S --- .../io/serialization/LineEndingOptions.java | 6 +++ .../io/serialization/PrettyPrintOptions.java | 39 ++++++++++++++++ .../io/serialization/UsesPrefixOptions.java | 32 +++++++++++++ .../canonical/RDFC10SerializerOptions.java | 8 ++-- .../nquads/NQuadsSerializerOptions.java | 8 ++-- .../ntriples/NTriplesSerializerOptions.java | 8 ++-- ...tion.java => RDFXMLSerializerOptions.java} | 34 ++++++++------ .../trig/TriGSerializerOptions.java | 8 ++-- .../turtle/TurtleSerializerOptions.java | 8 ++-- .../io/parser/rdfxml/RDFXMLCircularTest.java | 4 +- .../rdfxml/RDFXMLSerializerTest.java | 30 ++++++------ .../serialization/rdfxml/XmlConfigTest.java | 46 +++++++++---------- 12 files changed, 157 insertions(+), 74 deletions(-) create mode 100644 src/main/java/fr/inria/corese/core/next/api/io/serialization/LineEndingOptions.java create mode 100644 src/main/java/fr/inria/corese/core/next/api/io/serialization/PrettyPrintOptions.java create mode 100644 src/main/java/fr/inria/corese/core/next/api/io/serialization/UsesPrefixOptions.java rename src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/{RDFXMLSerializerOption.java => RDFXMLSerializerOptions.java} (93%) diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/LineEndingOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serialization/LineEndingOptions.java new file mode 100644 index 000000000..6fedb34f4 --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/api/io/serialization/LineEndingOptions.java @@ -0,0 +1,6 @@ +package fr.inria.corese.core.next.api.io.serialization; + +public interface LineEndingOptions { + + String getLineEnding(); +} diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/PrettyPrintOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serialization/PrettyPrintOptions.java new file mode 100644 index 000000000..b7ab0c79b --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/api/io/serialization/PrettyPrintOptions.java @@ -0,0 +1,39 @@ +package fr.inria.corese.core.next.api.io.serialization; + +public interface PrettyPrintOptions { + + /** + * Returns the string used for indentation when pretty-printing. + * + * @return The indentation string. + */ + String getIndent(); + + /** + * Checks if human-readable formatting (pretty-printing) is enabled. + * + * @return {@code true} if pretty-printing is enabled, {@code false} otherwise. + */ + boolean prettyPrint(); + + /** + * Returns the maximum desired line length before the serializer attempts to break lines. + * + * @return The maximum line length. + */ + int getMaxLineLength(); + + /** + * Checks if subjects should be sorted alphabetically in the output. + * + * @return {@code true} if subject sorting is enabled, {@code false} otherwise. + */ + boolean sortSubjects(); + + /** + * Checks if predicates should be sorted alphabetically within a subject group. + * + * @return {@code true} if predicate sorting is enabled, {@code false} otherwise. + */ + boolean sortPredicates(); +} diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/UsesPrefixOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serialization/UsesPrefixOptions.java new file mode 100644 index 000000000..7ccbbb3b7 --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/api/io/serialization/UsesPrefixOptions.java @@ -0,0 +1,32 @@ +package fr.inria.corese.core.next.api.io.serialization; + +import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; +import fr.inria.corese.core.next.impl.io.serialization.option.PrefixOrderingEnum; + +public interface UsesPrefixOptions { + + /** + * Checks if prefix declarations should be used for compact IRIs. + * + * @return {@code true} if prefixes are used, {@code false} otherwise. + */ + boolean usePrefixes(); + /** + * Checks if the serializer should automatically discover and declare prefixes. + * + * @return {@code true} if auto-declaration is enabled, {@code false} otherwise. + */ + boolean autoDeclarePrefixes(); + /** + * Returns the policy for ordering prefix declarations. + * + * @return The {@link PrefixOrderingEnum} for prefix ordering. + */ + public PrefixOrderingEnum getPrefixOrdering(); + /** + * Returns an unmodifiable map of custom URI prefixes. + * + * @return The {@link PrefixHandler} managing all prefix mappings. + */ + public PrefixHandler getPrefixHandler(); +} diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/canonical/RDFC10SerializerOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/canonical/RDFC10SerializerOptions.java index afc6ef596..5615d78a4 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/canonical/RDFC10SerializerOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/canonical/RDFC10SerializerOptions.java @@ -1,17 +1,17 @@ package fr.inria.corese.core.next.impl.io.serialization.canonical; -import fr.inria.corese.core.next.impl.io.serialization.option.AbstractSerializerOption; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractSerializerOptions; /** * Configuration for Canonical RDF serialization format (RDFC-1.0). - * This class extends {@link AbstractSerializerOption} and provides specific defaults + * This class extends {@link AbstractSerializerOptions} and provides specific defaults * and options tailored for the RDFC-10 canonicalization algorithm. * It includes options relevant to blank node canonicalization, such as the hashing algorithm * to use, the depth factor for graph isomorphism, and the permutation limit. * Use the {@link Builder} class to create instances of {@code CanonicalOption}. * A predefined default configuration is available via {@link #defaultConfig()}. */ -public class RDFC10SerializerOptions extends AbstractSerializerOption { +public class RDFC10SerializerOptions extends AbstractSerializerOptions { /** * Enumeration for the supported hashing algorithms. @@ -74,7 +74,7 @@ public int getPermutationLimit() { * Provides a fluent API for constructing {@code CanonicalOption} instances with default values * specific to the Canonical RDF format. */ - public static class Builder extends AbstractSerializerOption.AbstractBuilder { + public static class Builder extends AbstractSerializerOptions.AbstractBuilder { private HashAlgorithm hashAlgorithm = HashAlgorithm.SHA_256; private int depthFactor = 5; private int permutationLimit = 50000; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/nquads/NQuadsSerializerOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/nquads/NQuadsSerializerOptions.java index 1367d6697..935f2f752 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/nquads/NQuadsSerializerOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/nquads/NQuadsSerializerOptions.java @@ -1,16 +1,16 @@ package fr.inria.corese.core.next.impl.io.serialization.nquads; -import fr.inria.corese.core.next.impl.io.serialization.option.AbstractNFamilyOption; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractNFamilyOptions; /** * Configuration for N-Quads serialization format. - * This class extends {@link AbstractNFamilyOption} and provides specific defaults + * This class extends {@link AbstractNFamilyOptions} and provides specific defaults * and options tailored for N-Quads, which extends N-Quads with named graphs. * *

Use the {@link Builder} class to create instances of {@code NQuadsConfig}. * A predefined default configuration is available via {@link #defaultConfig()}.

*/ -public class NQuadsSerializerOptions extends AbstractNFamilyOption { +public class NQuadsSerializerOptions extends AbstractNFamilyOptions { /** * Protected constructor to be used by the {@link Builder}. @@ -26,7 +26,7 @@ protected NQuadsSerializerOptions(Builder builder) { * Provides a fluent API for constructing {@code NQuadsConfig} instances with default values * specific to the N-Quads format. */ - public static class Builder extends AbstractNFamilyOption.AbstractNFamilyBuilder { + public static class Builder extends AbstractNFamilyOptions.AbstractNFamilyBuilder { /** * Default constructor initializes all options with their default values for N-Quads. */ diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/ntriples/NTriplesSerializerOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/ntriples/NTriplesSerializerOptions.java index b611d2425..4cdec4029 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/ntriples/NTriplesSerializerOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/ntriples/NTriplesSerializerOptions.java @@ -1,16 +1,16 @@ package fr.inria.corese.core.next.impl.io.serialization.ntriples; -import fr.inria.corese.core.next.impl.io.serialization.option.AbstractNFamilyOption; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractNFamilyOptions; /** * Configuration for N-Triples serialization format. - * This class extends {@link AbstractNFamilyOption} and provides specific defaults + * This class extends {@link AbstractNFamilyOptions} and provides specific defaults * and options tailored for N-Triples, which is a simple, line-oriented format. * *

Use the {@link Builder} class to create instances of {@code NTriplesConfig}. * A predefined default configuration is available via {@link #defaultConfig()}.

*/ -public class NTriplesSerializerOptions extends AbstractNFamilyOption { +public class NTriplesSerializerOptions extends AbstractNFamilyOptions { /** * Protected constructor to be used by the {@link Builder}. @@ -26,7 +26,7 @@ protected NTriplesSerializerOptions(Builder builder) { * Provides a fluent API for constructing {@code NTriplesConfig} instances with default values * specific to the N-Triples format. */ - public static class Builder extends AbstractNFamilyOption.AbstractNFamilyBuilder { + public static class Builder extends AbstractNFamilyOptions.AbstractNFamilyBuilder { /** * Default constructor initializes all options with their default values for N-Triples. */ diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOption.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOptions.java similarity index 93% rename from src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOption.java rename to src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOptions.java index b6855b61b..eb4a03fd0 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOption.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOptions.java @@ -1,7 +1,8 @@ package fr.inria.corese.core.next.impl.io.serialization.rdfxml; +import fr.inria.corese.core.next.api.io.serialization.PrettyPrintOptions; import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; -import fr.inria.corese.core.next.impl.io.serialization.option.AbstractSerializerOption; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.option.LiteralDatatypePolicyEnum; import fr.inria.corese.core.next.impl.io.serialization.option.PrefixOrderingEnum; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; @@ -11,13 +12,13 @@ /** * Configuration for RDF/XML serialization format. - * This class extends {@link AbstractSerializerOption} directly as RDF/XML has + * This class extends {@link AbstractSerializerOptions} directly as RDF/XML has * distinct serialization characteristics not shared by the Turtle or N-Family formats. * - *

Use the {@link Builder} class to create instances of {@code RDFXMLSerializerOption}. + *

Use the {@link Builder} class to create instances of {@code RDFXMLSerializerOptions}. * A predefined default configuration is available via {@link #defaultConfig()}.

*/ -public class RDFXMLSerializerOption extends AbstractSerializerOption { +public class RDFXMLSerializerOptions extends AbstractSerializerOptions implements PrettyPrintOptions { /** * Whether prefix declarations (e.g., `xmlns:prefix="uri"`) should be used for compact IRIs. @@ -76,7 +77,7 @@ public class RDFXMLSerializerOption extends AbstractSerializerOption { * * @param builder The builder instance containing the desired configuration values. */ - protected RDFXMLSerializerOption(Builder builder) { + protected RDFXMLSerializerOptions(Builder builder) { super(builder); this.usePrefixes = builder.usePrefixes; @@ -144,6 +145,7 @@ public Map getCustomPrefixes() { * * @return {@code true} if pretty-printing is enabled, {@code false} otherwise. */ + @Override public boolean prettyPrint() { return prettyPrint; } @@ -153,6 +155,7 @@ public boolean prettyPrint() { * * @return The indentation string. */ + @Override public String getIndent() { return indent; } @@ -162,6 +165,7 @@ public String getIndent() { * * @return The maximum line length. */ + @Override public int getMaxLineLength() { return maxLineLength; } @@ -171,6 +175,7 @@ public int getMaxLineLength() { * * @return {@code true} if subject sorting is enabled, {@code false} otherwise. */ + @Override public boolean sortSubjects() { return sortSubjects; } @@ -180,6 +185,7 @@ public boolean sortSubjects() { * * @return {@code true} if predicate sorting is enabled, {@code false} otherwise. */ + @Override public boolean sortPredicates() { return sortPredicates; } @@ -194,11 +200,11 @@ public boolean useMultilineLiterals() { } /** - * Public Builder for {@link RDFXMLSerializerOption}. - * Provides a fluent API for constructing {@code RDFXMLSerializerOption} instances with default values + * Public Builder for {@link RDFXMLSerializerOptions}. + * Provides a fluent API for constructing {@code RDFXMLSerializerOptions} instances with default values * specific to the RDF/XML format. */ - public static class Builder extends AbstractSerializerOption.AbstractBuilder { + public static class Builder extends AbstractSerializerOptions.AbstractBuilder { protected boolean usePrefixes = true; protected boolean autoDeclarePrefixes = true; protected PrefixOrderingEnum prefixOrdering = PrefixOrderingEnum.ALPHABETICAL; @@ -364,13 +370,13 @@ public Builder useMultilineLiterals(boolean useMultilineLiterals) { } /** - * Builds and returns a new {@link RDFXMLSerializerOption} instance with the current builder settings. + * Builds and returns a new {@link RDFXMLSerializerOptions} instance with the current builder settings. * - * @return A new {@code RDFXMLSerializerOption} instance. + * @return A new {@code RDFXMLSerializerOptions} instance. */ @Override - public RDFXMLSerializerOption build() { - return new RDFXMLSerializerOption(this); + public RDFXMLSerializerOptions build() { + return new RDFXMLSerializerOptions(this); } } @@ -379,9 +385,9 @@ public RDFXMLSerializerOption build() { * This provides a convenient way to get a standard RDF/XML configuration without * manually building it. * - * @return A {@code RDFXMLSerializerOption} instance with default settings. + * @return A {@code RDFXMLSerializerOptions} instance with default settings. */ - public static RDFXMLSerializerOption defaultConfig() { + public static RDFXMLSerializerOptions defaultConfig() { return new Builder().build(); } } \ No newline at end of file diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializerOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializerOptions.java index 02633159c..31adeb113 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializerOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializerOptions.java @@ -1,17 +1,17 @@ package fr.inria.corese.core.next.impl.io.serialization.trig; -import fr.inria.corese.core.next.impl.io.serialization.option.AbstractTFamilyOption; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractTFamilyOptions; import fr.inria.corese.core.next.impl.io.serialization.option.BlankNodeStyleEnum; /** * Configuration for TriG serialization format. - * This class extends {@link AbstractTFamilyOption} and provides specific defaults + * This class extends {@link AbstractTFamilyOptions} and provides specific defaults * and options tailored for TriG, which extends Turtle with named graphs. * *

Use the {@link Builder} class to create instances of {@code TriGConfig}. * A predefined default configuration is available via {@link #defaultConfig()}.

*/ -public class TriGSerializerOptions extends AbstractTFamilyOption { +public class TriGSerializerOptions extends AbstractTFamilyOptions { /** * Protected constructor to be used by the {@link Builder}. @@ -27,7 +27,7 @@ protected TriGSerializerOptions(Builder builder) { * Provides a fluent API for constructing {@code TriGConfig} instances with default values * specific to the TriG format. */ - public static class Builder extends AbstractTFamilyOption.AbstractTFamilyBuilder { + public static class Builder extends AbstractTFamilyOptions.AbstractTFamilyBuilder { /** * Default constructor initializes all options with their default values for TriG. */ diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerOptions.java index bfbc71624..d20f960f9 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerOptions.java @@ -1,17 +1,17 @@ package fr.inria.corese.core.next.impl.io.serialization.turtle; -import fr.inria.corese.core.next.impl.io.serialization.option.AbstractTFamilyOption; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractTFamilyOptions; import fr.inria.corese.core.next.impl.io.serialization.option.BlankNodeStyleEnum; /** * Configuration for Turtle serialization format. - * This class extends {@link AbstractTFamilyOption} and provides specific defaults + * This class extends {@link AbstractTFamilyOptions} and provides specific defaults * and options tailored for Turtle, such as using collections and anonymous blank nodes. * *

Use the {@link Builder} class to create instances of {@code TurtleConfig}. * A predefined default configuration is available via {@link #defaultConfig()}.

*/ -public class TurtleSerializerOptions extends AbstractTFamilyOption { +public class TurtleSerializerOptions extends AbstractTFamilyOptions { /** * Protected constructor to be used by the {@link Builder}. @@ -27,7 +27,7 @@ protected TurtleSerializerOptions(Builder builder) { * Provides a fluent API for constructing {@code TurtleConfig} instances with default values * specific to the Turtle format. */ - public static class Builder extends AbstractTFamilyOption.AbstractTFamilyBuilder { + public static class Builder extends AbstractTFamilyOptions.AbstractTFamilyBuilder { /** * Default constructor initializes all options with their default values for Turtle. */ diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/rdfxml/RDFXMLCircularTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/rdfxml/RDFXMLCircularTest.java index 997cc0a64..1f77ec85e 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/rdfxml/RDFXMLCircularTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/rdfxml/RDFXMLCircularTest.java @@ -38,7 +38,7 @@ class RDFXMLCircularTest { private ValueFactory valueFactory; private fr.inria.corese.core.next.api.io.serialization.SerializerFactory serializerFactory; private ParserFactory parserFactory; - private RDFXMLSerializerOption defaultConfig; + private RDFXMLSerializerOptions defaultConfig; // Test data constants private static final String EXAMPLE_NS = "http://example.org/"; @@ -60,7 +60,7 @@ void setUp() { valueFactory = new CoreseAdaptedValueFactory(); serializerFactory = new SerializerFactory(); parserFactory = new ParserFactory(); - defaultConfig = RDFXMLSerializerOption.defaultConfig(); + defaultConfig = RDFXMLSerializerOptions.defaultConfig(); } /** diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java index 777d0bf5a..6713b69e1 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java @@ -28,7 +28,7 @@ class RDFXMLSerializerTest { @Mock private Model mockModel; - RDFXMLSerializerOption mockConfig; + RDFXMLSerializerOptions mockConfig; private TestStatementFactory factory; private StringWriter writer; private AutoCloseable closeable; @@ -58,7 +58,7 @@ void shouldSerializeSimpleIriTriple() throws SerializationException { when(mockModel.stream()).thenReturn(Stream.of(stmt)); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .autoDeclarePrefixes(true) .usePrefixes(true) .addPrefix("foaf", "http://xmlns.com/foaf/0.1/") @@ -93,7 +93,7 @@ void shouldHandleBlankNodeSubject() throws SerializationException { when(mockModel.stream()).thenReturn(Stream.of(stmt)); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .stableBlankNodeIds(true) .addPrefix("foaf", "http://xmlns.com/foaf/0.1/") .prefixOrdering(PrefixOrderingEnum.ALPHABETICAL) @@ -126,7 +126,7 @@ void shouldHandleBlankNodeObject() throws SerializationException { when(mockModel.stream()).thenReturn(Stream.of(stmt)); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .stableBlankNodeIds(true) .addPrefix("dc", "http://purl.org/dc/elements/1.1/") .prefixOrdering(PrefixOrderingEnum.ALPHABETICAL) @@ -159,7 +159,7 @@ void shouldSerializeLiteralWithStringDatatypeMinimalPolicy() throws Serializatio when(mockModel.stream()).thenReturn(Stream.of(stmt)); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .literalDatatypePolicy(LiteralDatatypePolicyEnum.MINIMAL) .addPrefix("foaf", "http://xmlns.com/foaf/0.1/") .prefixOrdering(PrefixOrderingEnum.ALPHABETICAL) @@ -190,7 +190,7 @@ void shouldSerializeLiteralWithCustomDatatypeMinimalPolicy() throws Serializatio when(mockModel.stream()).thenReturn(Stream.of(stmt)); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .literalDatatypePolicy(LiteralDatatypePolicyEnum.MINIMAL) .addPrefix("ex", "http://example.org/vocabulary/") .addPrefix("xsd", "http://www.w3.org/2001/XMLSchema#") @@ -222,7 +222,7 @@ void shouldSerializeLiteralWithLanguage() throws SerializationException { when(mockModel.stream()).thenReturn(Stream.of(stmt)); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .addPrefix("dc", "http://purl.org/dc/elements/1.1/") .prefixOrdering(PrefixOrderingEnum.ALPHABETICAL) .build(); @@ -259,7 +259,7 @@ void shouldRespectPrefixOrderingDefault() throws SerializationException { when(mockModel.stream()).thenReturn(Stream.of(stmt1, stmt2)); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .addPrefix("exorg", "http://ex.org/") .addPrefix("excom", "http://ex.com/") .prefixOrdering(PrefixOrderingEnum.USAGE_ORDER) @@ -301,7 +301,7 @@ void shouldSortSubjectsAlphabetically() throws SerializationException { when(mockModel.stream()).thenReturn(Stream.of(stmt1, stmt2)); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .sortSubjects(true) .addPrefix("ex", "http://ex.org/") .prefixOrdering(PrefixOrderingEnum.ALPHABETICAL) @@ -337,7 +337,7 @@ void shouldEscapeXmlAttributeValues() throws SerializationException { when(mockModel.stream()).thenReturn(Stream.of(stmt)); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .prefixOrdering(PrefixOrderingEnum.ALPHABETICAL) .build(); @@ -367,7 +367,7 @@ void shouldEscapeXmlContentValues() throws SerializationException { when(mockModel.stream()).thenReturn(Stream.of(stmt)); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .literalDatatypePolicy(LiteralDatatypePolicyEnum.ALWAYS_TYPED) .addPrefix("ex", "http://example.org/") .prefixOrdering(PrefixOrderingEnum.ALPHABETICAL) @@ -401,7 +401,7 @@ void shouldNotAutoDeclarePrefixesIfDisabled() throws SerializationException { when(mockModel.stream()).thenReturn(Stream.of(stmt)); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .autoDeclarePrefixes(false) .usePrefixes(true) .prefixOrdering(PrefixOrderingEnum.ALPHABETICAL) @@ -433,7 +433,7 @@ void shouldNotUsePrefixesIfDisabled() throws SerializationException { when(mockModel.stream()).thenReturn(Stream.of(stmt)); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .usePrefixes(false) .autoDeclarePrefixes(true) .prefixOrdering(PrefixOrderingEnum.ALPHABETICAL) @@ -471,7 +471,7 @@ void shouldNotGenerateStableBlankNodeIds() throws SerializationException { when(mockModel.stream()).thenReturn(Stream.of(stmt1, stmt2)); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .stableBlankNodeIds(false) .sortSubjects(true) .addPrefix("ex", "http://example.org/") @@ -502,7 +502,7 @@ void shouldNotGenerateStableBlankNodeIds() throws SerializationException { void shouldHandleEmptyModel() throws SerializationException { when(mockModel.stream()).thenReturn(Stream.empty()); - RDFXMLSerializerOption testConfig = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions testConfig = new RDFXMLSerializerOptions.Builder() .prefixOrdering(PrefixOrderingEnum.ALPHABETICAL) .build(); diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/XmlConfigTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/XmlConfigTest.java index 126406e5e..dbf9085f2 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/XmlConfigTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/XmlConfigTest.java @@ -17,7 +17,7 @@ import static org.junit.jupiter.api.Assertions.*; /** - * Unit tests for the {@link RDFXMLSerializerOption} class. + * Unit tests for the {@link RDFXMLSerializerOptions} class. * These tests verify the default configuration settings and the functionality * of the builder pattern for customizing RDF/XML serialization options. */ @@ -26,7 +26,7 @@ class XmlConfigTest { @Test @DisplayName("defaultConfig() should return a config with expected RDF/XML defaults") void defaultConfig_shouldReturnExpectedDefaults() { - RDFXMLSerializerOption config = RDFXMLSerializerOption.defaultConfig(); + RDFXMLSerializerOptions config = RDFXMLSerializerOptions.defaultConfig(); assertNotNull(config, "Default config should not be null"); @@ -64,7 +64,7 @@ void defaultConfig_shouldReturnExpectedDefaults() { @Test @DisplayName("Builder should allow overriding usePrefixes") void builder_shouldAllowOverridingUsePrefixes() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .usePrefixes(false) .build(); assertFalse(config.usePrefixes(), "usePrefixes should be overridden to false"); @@ -73,7 +73,7 @@ void builder_shouldAllowOverridingUsePrefixes() { @Test @DisplayName("Builder should allow overriding autoDeclarePrefixes") void builder_shouldAllowOverridingAutoDeclarePrefixes() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .autoDeclarePrefixes(false) .build(); assertFalse(config.autoDeclarePrefixes(), "autoDeclarePrefixes should be overridden to false"); @@ -82,7 +82,7 @@ void builder_shouldAllowOverridingAutoDeclarePrefixes() { @Test @DisplayName("Builder should allow overriding prefixOrdering") void builder_shouldAllowOverridingPrefixOrdering() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .prefixOrdering(PrefixOrderingEnum.USAGE_ORDER) .build(); assertEquals(PrefixOrderingEnum.USAGE_ORDER, config.getPrefixOrdering(), "prefixOrdering should be overridden to USAGE_ORDER"); @@ -94,7 +94,7 @@ void builder_shouldAllowAddingCustomPrefixes() { String customPrefix = "my"; String customNamespace = "http://my.example.org/"; - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .addPrefix(customPrefix, customNamespace) .build(); @@ -114,7 +114,7 @@ void builder_shouldAllowSettingCustomPrefixHandler() { customHandler.setPrefix("ex", "http://example.org/"); customHandler.setPrefix("custom", "http://custom.org/"); - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .prefixHandler(customHandler) .build(); @@ -132,7 +132,7 @@ void builder_shouldAllowAddingMultiplePrefixes() { customPrefixes.put("ex", "http://example.org/"); customPrefixes.put("custom", "http://custom.org/"); - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .addPrefixes(customPrefixes) .build(); @@ -146,7 +146,7 @@ void builder_shouldAllowAddingMultiplePrefixes() { @Test @DisplayName("Builder should allow overriding prettyPrint") void builder_shouldAllowOverridingPrettyPrint() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .prettyPrint(false) .build(); assertFalse(config.prettyPrint(), "prettyPrint should be overridden to false"); @@ -156,7 +156,7 @@ void builder_shouldAllowOverridingPrettyPrint() { @DisplayName("Builder should allow overriding indent") void builder_shouldAllowOverridingIndent() { String customIndent = "\t"; - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .indent(customIndent) .build(); assertEquals(customIndent, config.getIndent(), "indent should be overridden to custom value"); @@ -166,7 +166,7 @@ void builder_shouldAllowOverridingIndent() { @DisplayName("Builder should allow overriding maxLineLength") void builder_shouldAllowOverridingMaxLineLength() { int customLength = 120; - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .maxLineLength(customLength) .build(); assertEquals(customLength, config.getMaxLineLength(), "maxLineLength should be overridden to custom value"); @@ -175,7 +175,7 @@ void builder_shouldAllowOverridingMaxLineLength() { @Test @DisplayName("Builder should allow overriding sortSubjects") void builder_shouldAllowOverridingSortSubjects() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .sortSubjects(true) .build(); assertTrue(config.sortSubjects(), "sortSubjects should be overridden to true"); @@ -184,7 +184,7 @@ void builder_shouldAllowOverridingSortSubjects() { @Test @DisplayName("Builder should allow overriding sortPredicates") void builder_shouldAllowOverridingSortPredicates() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .sortPredicates(true) .build(); assertTrue(config.sortPredicates(), "sortPredicates should be overridden to true"); @@ -193,7 +193,7 @@ void builder_shouldAllowOverridingSortPredicates() { @Test @DisplayName("Builder should allow overriding useMultilineLiterals") void builder_shouldAllowOverridingUseMultilineLiterals() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .useMultilineLiterals(false) .build(); assertFalse(config.useMultilineLiterals(), "useMultilineLiterals should be overridden to false"); @@ -202,7 +202,7 @@ void builder_shouldAllowOverridingUseMultilineLiterals() { @Test @DisplayName("Builder should allow overriding strictMode") void builder_shouldAllowOverridingStrictMode() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .strictMode(false) .build(); assertFalse(config.isStrictMode(), "strictMode should be overridden to false"); @@ -211,7 +211,7 @@ void builder_shouldAllowOverridingStrictMode() { @Test @DisplayName("Builder should allow overriding escapeUnicode") void builder_shouldAllowOverridingEscapeUnicode() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .escapeUnicode(true) .build(); assertTrue(config.escapeUnicode(), "escapeUnicode should be overridden to true"); @@ -220,7 +220,7 @@ void builder_shouldAllowOverridingEscapeUnicode() { @Test @DisplayName("Builder should allow overriding literalDatatypePolicy") void builder_shouldAllowOverridingLiteralDatatypePolicy() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .literalDatatypePolicy(LiteralDatatypePolicyEnum.MINIMAL) .build(); assertEquals(LiteralDatatypePolicyEnum.MINIMAL, config.getLiteralDatatypePolicy(), "literalDatatypePolicy should be overridden to MINIMAL"); @@ -230,7 +230,7 @@ void builder_shouldAllowOverridingLiteralDatatypePolicy() { @DisplayName("Builder should allow setting baseIRI") void builder_shouldAllowSettingBaseIRI() { String testBaseIRI = "http://example.org/base/"; - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .baseIRI(testBaseIRI) .build(); assertEquals(testBaseIRI, config.getBaseIRI(), "baseIRI should be set correctly"); @@ -240,7 +240,7 @@ void builder_shouldAllowSettingBaseIRI() { @DisplayName("Builder should allow overriding lineEnding") void builder_shouldAllowOverridingLineEnding() { String customLineEnding = "\r\n"; - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .lineEnding(customLineEnding) .build(); assertEquals(customLineEnding, config.getLineEnding(), "lineEnding should be overridden to custom value"); @@ -249,7 +249,7 @@ void builder_shouldAllowOverridingLineEnding() { @Test @DisplayName("Builder should allow overriding validateURIs") void builder_shouldAllowOverridingValidateURIs() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .validateURIs(true) .build(); assertTrue(config.validateURIs(), "validateURIs should be overridden to true"); @@ -258,7 +258,7 @@ void builder_shouldAllowOverridingValidateURIs() { @Test @DisplayName("Builder should allow overriding stableBlankNodeIds") void builder_shouldAllowOverridingStableBlankNodeIds() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .stableBlankNodeIds(false) .build(); assertFalse(config.stableBlankNodeIds(), "stableBlankNodeIds should be overridden to false"); @@ -267,7 +267,7 @@ void builder_shouldAllowOverridingStableBlankNodeIds() { @Test @DisplayName("Builder should allow overriding includeContext") void builder_shouldAllowOverridingIncludeContext() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .includeContext(true) .build(); assertTrue(config.includeContext(), "includeContext should be overridden to true"); @@ -276,7 +276,7 @@ void builder_shouldAllowOverridingIncludeContext() { @Test @DisplayName("Builder should maintain default prefixes when adding custom ones") void builder_shouldMaintainDefaultPrefixesWhenAddingCustomOnes() { - RDFXMLSerializerOption config = new RDFXMLSerializerOption.Builder() + RDFXMLSerializerOptions config = new RDFXMLSerializerOptions.Builder() .addPrefix("ex", "http://example.org/") .build(); From f8b6860c95a1038e4694f3433eb5738dedd42a7b Mon Sep 17 00:00:00 2001 From: Pierre Maillot Date: Mon, 8 Dec 2025 17:10:51 +0100 Subject: [PATCH 02/12] Changing the config behaviour to interface-based checks --- .../base/AbstractGraphSerializer.java | 117 +++++++----------- .../base/AbstractLineBasedSerializer.java | 9 +- ...ption.java => AbstractNFamilyOptions.java} | 16 +-- ...on.java => AbstractSerializerOptions.java} | 13 +- ...ption.java => AbstractTFamilyOptions.java} | 25 ++-- .../io/serialization/trig/TriGSerializer.java | 78 +++++++----- .../turtle/TurtleSerializer.java | 26 +++- 7 files changed, 150 insertions(+), 134 deletions(-) rename src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/{AbstractNFamilyOption.java => AbstractNFamilyOptions.java} (82%) rename src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/{AbstractSerializerOption.java => AbstractSerializerOptions.java} (96%) rename src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/{AbstractTFamilyOption.java => AbstractTFamilyOptions.java} (96%) diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java index 83c88aa40..356e877dc 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java @@ -1,6 +1,8 @@ package fr.inria.corese.core.next.impl.io.serialization.base; import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.io.serialization.PrettyPrintOptions; +import fr.inria.corese.core.next.api.io.serialization.UsesPrefixOptions; import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; import fr.inria.corese.core.next.impl.common.vocabulary.*; @@ -28,8 +30,8 @@ * *

Note: Many features related to compact syntax, pretty-printing, and advanced * prefix management are specific to Turtle Trig formats and require the - * provided {@link AbstractSerializerOption} to be an instance of - * {@link AbstractSerializerOption} at runtime. An {@link IllegalStateException} + * provided {@link AbstractSerializerOptions} to be an instance of + * {@link AbstractSerializerOptions} at runtime. An {@link IllegalStateException} * will be thrown if an incompatible configuration is used for such features.

*/ public abstract class AbstractGraphSerializer implements RDFSerializer { @@ -40,7 +42,7 @@ public abstract class AbstractGraphSerializer implements RDFSerializer { protected static final Logger logger = LoggerFactory.getLogger(AbstractGraphSerializer.class); protected final Model model; - protected final AbstractSerializerOption option; + protected AbstractSerializerOptions option; protected final Map iriToPrefixMapping; protected final Map prefixToIriMapping; protected final Set consumedBlankNodes; @@ -50,10 +52,10 @@ public abstract class AbstractGraphSerializer implements RDFSerializer { * Constructs a new abstract TriG/Turtle serializer instance. * * @param model the {@link Model} to serialize. Must not be null. - * @param option the {@link AbstractSerializerOption} to use for serialization. Must not be null. + * @param option the {@link AbstractSerializerOptions} to use for serialization. Must not be null. * @throws NullPointerException if the provided model or configuration is null. */ - protected AbstractGraphSerializer(Model model, AbstractSerializerOption option) { + protected AbstractGraphSerializer(Model model, AbstractSerializerOptions option) { this.model = Objects.requireNonNull(model, "The model cannot be null"); this.option = Objects.requireNonNull(option, "The configuration cannot be null"); this.iriToPrefixMapping = new HashMap<>(); @@ -63,28 +65,12 @@ protected AbstractGraphSerializer(Model model, AbstractSerializerOption option) initializePrefixes(); } - /** - * Helper method to safely cast the generic config to AbstractTFamilyConfig. - * This should be called before accessing any methods specific to AbstractTFamilyConfig. - * - * @return The config cast to AbstractTFamilyConfig. - * @throws SerializationException if the config is not an instance of AbstractTFamilyConfig. - */ - private AbstractTFamilyOption getTFamilyOption() { - if (!(option instanceof AbstractTFamilyOption)) { - throw new SerializationException("Current serializer configuration is not an instance of AbstractTFamilyOption. " + - "Features like prefixes, compact syntax, and pretty-printing are only available for T-Family formats.", this.getFormatName()); - } - return (AbstractTFamilyOption) option; - } - /** * Initializes prefix mappings by adding custom prefixes from the configuration. */ private void initializePrefixes() { - if (option instanceof AbstractTFamilyOption && getTFamilyOption().usePrefixes()) { - AbstractTFamilyOption tFamilyOption = getTFamilyOption(); - PrefixHandler prefixHandler = tFamilyOption.getPrefixHandler(); + if (option instanceof UsesPrefixOptions prefixOptions && prefixOptions.usePrefixes()) { + PrefixHandler prefixHandler = prefixOptions.getPrefixHandler(); for (String prefix : prefixHandler.getPrefixes()) { String namespace = prefixHandler.getNamespace(prefix); @@ -142,9 +128,9 @@ protected void writeHeader(Writer writer) throws IOException { option.getLineEnding())); } - if (option instanceof AbstractSerializerOption - && getTFamilyOption().usePrefixes() - && getTFamilyOption().autoDeclarePrefixes()) { + if (option instanceof UsesPrefixOptions prefixOptions + && prefixOptions.usePrefixes() + && prefixOptions.autoDeclarePrefixes()) { collectUsedNamespaces(); } @@ -191,23 +177,23 @@ protected void collectUsedNamespaces() { * @throws IOException if an I/O error occurs. */ protected void writePrefixDeclarations(Writer writer) throws IOException { - AbstractTFamilyOption tFamilyConfig = getTFamilyOption(); - - List prefixes = new ArrayList<>(prefixToIriMapping.keySet()); + if(this.option instanceof UsesPrefixOptions prefixOptions) { + List prefixes = new ArrayList<>(prefixToIriMapping.keySet()); - if (tFamilyConfig.getPrefixOrdering() == PrefixOrderingEnum.ALPHABETICAL) { - Collections.sort(prefixes); - } + if (prefixOptions.getPrefixOrdering() == PrefixOrderingEnum.ALPHABETICAL) { + Collections.sort(prefixes); + } - for (String prefix : prefixes) { - writer.write(String.format("@prefix %s: <%s> .%s", - prefix, - prefixToIriMapping.get(prefix), - option.getLineEnding())); - } + for (String prefix : prefixes) { + writer.write(String.format("@prefix %s: <%s> .%s", + prefix, + prefixToIriMapping.get(prefix), + option.getLineEnding())); + } - if (!prefixes.isEmpty() || option.getBaseIRI() != null) { - writer.write(option.getLineEnding()); + if (!prefixes.isEmpty() || option.getBaseIRI() != null) { + writer.write(option.getLineEnding()); + } } } @@ -236,9 +222,7 @@ protected void writeSimpleStatements(Writer writer) throws IOException { * @throws IOException if an I/O error occurs. */ protected void writeStatement(Writer writer, Statement stmt) throws IOException { - AbstractTFamilyOption tFamilyConfig = getTFamilyOption(); - - String indent = tFamilyConfig.prettyPrint() ? tFamilyConfig.getIndent() : SerializationConstants.EMPTY_STRING; + String indent = this.option instanceof PrettyPrintOptions prettyOptions && prettyOptions.prettyPrint() ? prettyOptions.getIndent() : SerializationConstants.EMPTY_STRING; writer.write(indent); // Subject @@ -264,8 +248,7 @@ protected void writeStatement(Writer writer, Statement stmt) throws IOException * @throws IOException if an I/O error occurs. */ protected void writePredicate(Writer writer, Value predicate) throws IOException { - AbstractTFamilyOption tFamilyConfig = getTFamilyOption(); - if (tFamilyConfig.useRdfTypeShortcut() && predicate.equals(RDF.type.getIRI())) { + if (this.option instanceof AbstractTFamilyOptions tFamilyOptions && tFamilyOptions.useRdfTypeShortcut() && predicate.equals(RDF.type.getIRI())) { writer.write(SerializationConstants.RDF_TYPE_SHORTCUT); } else { writeValue(writer, predicate); @@ -303,12 +286,12 @@ protected void writeValue(Writer writer, Value value) throws IOException { boolean isSubject = model.stream().anyMatch(stmt -> stmt.getSubject().equals(bNode)); - if (!isSubject && option instanceof AbstractSerializerOption) { - if (getTFamilyOption().useCollections() && bNode.isBNode()) { + if (!isSubject && option instanceof AbstractTFamilyOptions abstractTFOptions) { + if (abstractTFOptions.useCollections() && bNode.isBNode()) { handled = writeRDFList(writer, bNode); } - if (!handled && getTFamilyOption().getBlankNodeStyle() == BlankNodeStyleEnum.ANONYMOUS && bNode.isBNode()) { + if (!handled && abstractTFOptions.getBlankNodeStyle() == BlankNodeStyleEnum.ANONYMOUS && bNode.isBNode()) { List properties = model.stream() .filter(stmt -> stmt.getSubject().equals(bNode)) .toList(); @@ -345,7 +328,7 @@ protected void writeIRI(Writer writer, IRI iri) throws IOException { } String prefixed = null; - if (option instanceof AbstractSerializerOption && getTFamilyOption().usePrefixes()) { + if (option instanceof UsesPrefixOptions prefixOptions && prefixOptions.usePrefixes()) { prefixed = getPrefixedName(iri.stringValue()); } @@ -369,8 +352,8 @@ protected void writeLiteral(Writer writer, Literal literal) throws IOException { String value = literal.stringValue(); boolean useTripleQuotes = false; - if (option instanceof AbstractTFamilyOption) { - useTripleQuotes = getTFamilyOption().useMultilineLiterals() && + if (option instanceof AbstractTFamilyOptions tFamilyOptions) { + useTripleQuotes = tFamilyOptions.useMultilineLiterals() && (value.contains(SerializationConstants.LINE_FEED) || value.contains(SerializationConstants.CARRIAGE_RETURN) || value.contains("\"\"\"")); } @@ -436,10 +419,8 @@ protected boolean shouldWriteDatatype(Literal literal) { * @throws IOException if an I/O error occurs. */ protected void writeInlineBlankNode(Writer writer, List properties) throws IOException { - AbstractTFamilyOption tFamilyConfig = getTFamilyOption(); - - String currentIndent = tFamilyConfig.prettyPrint() ? tFamilyConfig.getIndent() : SerializationConstants.EMPTY_STRING; - String propIndent = tFamilyConfig.prettyPrint() ? currentIndent + tFamilyConfig.getIndent() : ""; + String currentIndent = this.option instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.prettyPrint() ? prettyPrintOptions.getIndent() : SerializationConstants.EMPTY_STRING; + String propIndent = this.option instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.prettyPrint() ? currentIndent + prettyPrintOptions.getIndent() : ""; writer.write(SerializationConstants.BLANK_NODE_START); @@ -455,7 +436,7 @@ protected void writeInlineBlankNode(Writer writer, List properties) t } firstProperty = false; - if (tFamilyConfig.prettyPrint()) { + if (this.option instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.prettyPrint()) { writer.write(option.getLineEnding() + propIndent); } else { writer.write(SerializationConstants.SPACE); @@ -466,7 +447,7 @@ protected void writeInlineBlankNode(Writer writer, List properties) t writeValue(writer, stmt.getObject()); } - if (tFamilyConfig.prettyPrint() && !properties.isEmpty() && !firstProperty) { + if (this.option instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.prettyPrint() && !properties.isEmpty() && !firstProperty) { writer.write(option.getLineEnding() + currentIndent); } @@ -482,9 +463,7 @@ protected void writeInlineBlankNode(Writer writer, List properties) t * @throws IOException if an I/O error occurs. */ protected void writeOptimizedStatements(Writer writer) throws IOException { - AbstractTFamilyOption tFamilyConfig = getTFamilyOption(); - - Map> bySubject = tFamilyConfig.sortSubjects() ? + Map> bySubject = this.option instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.sortSubjects() ? new TreeMap<>(Comparator.comparing(Resource::stringValue)) : new HashMap<>(); @@ -493,12 +472,12 @@ protected void writeOptimizedStatements(Writer writer) throws IOException { .forEach(stmt -> bySubject.computeIfAbsent(stmt.getSubject(), k -> new ArrayList<>()).add(stmt)); for (Map.Entry> subjectEntry : bySubject.entrySet()) { - String indent = tFamilyConfig.prettyPrint() ? tFamilyConfig.getIndent() : SerializationConstants.EMPTY_STRING; + String indent = this.option instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.prettyPrint() ? prettyPrintOptions.getIndent() : SerializationConstants.EMPTY_STRING; writer.write(indent); writeValue(writer, subjectEntry.getKey()); writer.write(SerializationConstants.SPACE); - Map> byPredicate = tFamilyConfig.sortPredicates() ? + Map> byPredicate = this.option instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.sortPredicates() ? new TreeMap<>(Comparator.comparing(IRI::stringValue)) : new HashMap<>(); @@ -508,8 +487,8 @@ protected void writeOptimizedStatements(Writer writer) throws IOException { for (Map.Entry> predicateEntry : byPredicate.entrySet()) { if (!firstPredicate) { writer.write(SerializationConstants.SEMICOLON); - if (tFamilyConfig.prettyPrint()) { - writer.write(option.getLineEnding() + indent + tFamilyConfig.getIndent()); + if (this.option instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.prettyPrint()) { + writer.write(option.getLineEnding() + indent + prettyPrintOptions.getIndent()); } else { writer.write(SerializationConstants.SPACE); } @@ -523,8 +502,8 @@ protected void writeOptimizedStatements(Writer writer) throws IOException { for (Statement stmt : predicateEntry.getValue()) { if (!firstObject) { writer.write(SerializationConstants.COMMA); - if (tFamilyConfig.prettyPrint()) { - writer.write(option.getLineEnding() + indent + tFamilyConfig.getIndent() + tFamilyConfig.getIndent()); + if (this.option instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.prettyPrint()) { + writer.write(option.getLineEnding() + indent + prettyPrintOptions.getIndent() + prettyPrintOptions.getIndent()); } else { writer.write(SerializationConstants.SPACE); } @@ -638,19 +617,17 @@ protected boolean isConsumed(Value value) { * @return A {@link Set} of {@link Resource} representing the blank nodes that will be serialized inline. */ protected Set precomputeInlineBlankNodesAndLists() { - AbstractTFamilyOption tFamilyConfig = getTFamilyOption(); - Set precomputed = new HashSet<>(); for (Statement stmt : model) { if (stmt.getSubject().isBNode()) { Resource bNodeSubject = stmt.getSubject(); - if (tFamilyConfig.useCollections() && isRDFListHead(bNodeSubject)) { + if (this.option instanceof AbstractTFamilyOptions tFamilyOptions && tFamilyOptions.useCollections() && isRDFListHead(bNodeSubject)) { Set listNodes = detectListNodes(bNodeSubject); if (!listNodes.isEmpty()) { precomputed.addAll(listNodes); } } - if (tFamilyConfig.getBlankNodeStyle() == BlankNodeStyleEnum.ANONYMOUS) { + if (this.option instanceof AbstractTFamilyOptions tFamilyOptions && tFamilyOptions.getBlankNodeStyle() == BlankNodeStyleEnum.ANONYMOUS) { List properties = model.stream() .filter(s -> s.getSubject().equals(bNodeSubject)) .toList(); diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractLineBasedSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractLineBasedSerializer.java index 48081413f..061baf8e8 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractLineBasedSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractLineBasedSerializer.java @@ -2,13 +2,13 @@ import java.io.BufferedWriter; import java.io.IOException; -import java.io.UncheckedIOException; import java.io.Writer; import java.util.Collections; import java.util.Objects; import java.util.Set; import fr.inria.corese.core.next.impl.common.literal.XSD; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractSerializerOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,7 +21,6 @@ import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; import fr.inria.corese.core.next.impl.common.literal.RDF; import fr.inria.corese.core.next.impl.exception.SerializationException; -import fr.inria.corese.core.next.impl.io.serialization.option.AbstractSerializerOption; import fr.inria.corese.core.next.impl.io.serialization.option.LiteralDatatypePolicyEnum; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; @@ -38,16 +37,16 @@ public abstract class AbstractLineBasedSerializer implements RDFSerializer { private static final Logger logger = LoggerFactory.getLogger(AbstractLineBasedSerializer.class); protected final Model model; - protected final AbstractSerializerOption config; + protected final AbstractSerializerOptions config; /** * Constructs a new line-based serializer. * * @param model the {@link Model} to be serialized. Must not be null. - * @param config the {@link AbstractSerializerOption} to use for serialization. Must not be null. + * @param config the {@link AbstractSerializerOptions} to use for serialization. Must not be null. * @throws NullPointerException if the provided model or config is null. */ - protected AbstractLineBasedSerializer(Model model, AbstractSerializerOption config) { + protected AbstractLineBasedSerializer(Model model, AbstractSerializerOptions config) { this.model = Objects.requireNonNull(model, "Model cannot be null"); this.config = Objects.requireNonNull(config, "Configuration cannot be null"); } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractNFamilyOption.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractNFamilyOptions.java similarity index 82% rename from src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractNFamilyOption.java rename to src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractNFamilyOptions.java index 0ebf8efc3..a43616d09 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractNFamilyOption.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractNFamilyOptions.java @@ -2,7 +2,7 @@ /** * An abstract base class for serialization configurations of N-Family RDF formats (e.g., N-Triples, N-Quads). - * This class extends {@link AbstractSerializerOption} and provides a common foundation + * This class extends {@link AbstractSerializerOptions} and provides a common foundation * for formats that typically have simpler, line-based structures and specific default behaviors * regarding literal datatypes and character escaping. * @@ -10,7 +10,7 @@ * nested {@link AbstractNFamilyBuilder}. Subclasses are expected to extend this * configuration and its builder to add format-specific options.

*/ -public abstract class AbstractNFamilyOption extends AbstractSerializerOption { +public abstract class AbstractNFamilyOptions extends AbstractSerializerOptions { /** * Protected constructor to be used by concrete builder implementations. @@ -19,14 +19,14 @@ public abstract class AbstractNFamilyOption extends AbstractSerializerOption { * * @param builder The builder instance containing the desired configuration values. */ - protected AbstractNFamilyOption(AbstractNFamilyBuilder builder) { + protected AbstractNFamilyOptions(AbstractNFamilyBuilder builder) { super(builder); } /** - * An abstract base builder for {@link AbstractNFamilyOption}. + * An abstract base builder for {@link AbstractNFamilyOptions}. * This builder provides methods for setting N-Family serialization configuration options. - * It extends {@link AbstractSerializerOption.AbstractBuilder} and uses a recursive type + * It extends {@link AbstractSerializerOptions.AbstractBuilder} and uses a recursive type * parameter (`S`) to allow concrete subclass builders to return their own specific type, * enabling fluent API chaining. * @@ -36,7 +36,7 @@ protected AbstractNFamilyOption(AbstractNFamilyBuilder builder) { * @param The type of the concrete builder extending this abstract builder. */ public abstract static class AbstractNFamilyBuilder> - extends AbstractSerializerOption.AbstractBuilder { + extends AbstractSerializerOptions.AbstractBuilder { /** * Default constructor for the builder. @@ -52,11 +52,11 @@ protected AbstractNFamilyBuilder() { } /** - * Builds and returns a new {@link AbstractNFamilyOption} instance with the current builder settings. + * Builds and returns a new {@link AbstractNFamilyOptions} instance with the current builder settings. * This method must be implemented by concrete builder subclasses to return their specific configuration type. * * @return A new {@code AbstractNFamilyConfig} instance or a subclass instance. */ - public abstract AbstractNFamilyOption build(); + public abstract AbstractNFamilyOptions build(); } } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOption.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java similarity index 96% rename from src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOption.java rename to src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java index 353c229b5..c1da6edab 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOption.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java @@ -2,6 +2,7 @@ import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.api.io.common.BaseIRIOptions; +import fr.inria.corese.core.next.api.io.serialization.LineEndingOptions; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; import java.util.Objects; @@ -15,7 +16,7 @@ * nested {@link AbstractBuilder}. Subclasses are expected to extend this * configuration and its builder to add format-specific options.

*/ -public abstract class AbstractSerializerOption implements IOOptions , BaseIRIOptions { +public abstract class AbstractSerializerOptions implements IOOptions, BaseIRIOptions, LineEndingOptions { /** * The policy for how literal datatypes are printed. @@ -72,7 +73,7 @@ public abstract class AbstractSerializerOption implements IOOptions , BaseIRIOpt * @param builder The builder instance containing the desired configuration values. * @throws NullPointerException if any required field from the builder is null. */ - protected AbstractSerializerOption(AbstractBuilder builder) { + protected AbstractSerializerOptions(AbstractBuilder builder) { this.literalDatatypePolicy = Objects.requireNonNull(builder.literalDatatypePolicy, "Literal datatype policy cannot be null"); this.escapeUnicode = builder.escapeUnicode; this.trailingDot = builder.trailingDot; @@ -119,6 +120,7 @@ public boolean trailingDot() { * * @return The base IRI string, or {@code null} if no base IRI is specified. */ + @Override public String getBaseIRI() { return baseIRI; } @@ -137,6 +139,7 @@ public boolean stableBlankNodeIds() { * * @return The line ending string (e.g., `"\n"` for Unix, `"\r\n"` for Windows). */ + @Override public String getLineEnding() { return lineEnding; } @@ -169,7 +172,7 @@ public boolean includeContext() { } /** - * An abstract base builder for {@link AbstractSerializerOption}. + * An abstract base builder for {@link AbstractSerializerOptions}. * This builder provides methods for setting common serialization configuration options. * It uses a recursive type parameter (`S`) to allow concrete subclass builders * to return their own specific type, enabling fluent API chaining. @@ -290,12 +293,12 @@ public S includeContext(boolean include) { } /** - * Builds and returns a new {@link AbstractSerializerOption} instance with the current builder settings. + * Builds and returns a new {@link AbstractSerializerOptions} instance with the current builder settings. * This method must be implemented by concrete builder subclasses to return their specific configuration type. * * @return A new {@code AbstractSerializerConfig} instance or a subclass instance. */ - public abstract AbstractSerializerOption build(); + public abstract AbstractSerializerOptions build(); /** * Helper method to return the concrete builder instance for fluent API chaining. diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOption.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java similarity index 96% rename from src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOption.java rename to src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java index 735ec7edf..c2be59d44 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOption.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java @@ -1,5 +1,7 @@ package fr.inria.corese.core.next.impl.io.serialization.option; +import fr.inria.corese.core.next.api.io.serialization.PrettyPrintOptions; +import fr.inria.corese.core.next.api.io.serialization.UsesPrefixOptions; import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; @@ -8,14 +10,14 @@ /** * An abstract base class for serialization configurations of Turtle Trig RDF formats (e.g., Turtle, TriG). - * This class extends {@link AbstractSerializerOption} and introduces parameters specific to + * This class extends {@link AbstractSerializerOptions} and introduces parameters specific to * formats that utilize syntax sugar, pretty-printing, and collection syntax. * *

It enforces the use of the Builder pattern for construction through its * nested {@link AbstractTFamilyBuilder}. Subclasses are expected to extend this * configuration and its builder to add format-specific options.

*/ -public abstract class AbstractTFamilyOption extends AbstractSerializerOption { +public abstract class AbstractTFamilyOptions extends AbstractSerializerOptions implements UsesPrefixOptions, PrettyPrintOptions { /** * Whether prefix declarations (e.g., `@prefix`, `PREFIX`) should be used for compact IRIs. @@ -105,7 +107,7 @@ public abstract class AbstractTFamilyOption extends AbstractSerializerOption { * @throws NullPointerException if any required field from the builder is null. * @throws IllegalArgumentException if incompatible options (e.g., escapeUnicode and useMultilineLiterals) are enabled. */ - protected AbstractTFamilyOption(AbstractTFamilyBuilder builder) { + protected AbstractTFamilyOptions(AbstractTFamilyBuilder builder) { super(builder); this.usePrefixes = builder.usePrefixes; @@ -135,6 +137,7 @@ protected AbstractTFamilyOption(AbstractTFamilyBuilder builder) { * * @return {@code true} if prefixes are used, {@code false} otherwise. */ + @Override public boolean usePrefixes() { return usePrefixes; } @@ -144,6 +147,7 @@ public boolean usePrefixes() { * * @return {@code true} if auto-declaration is enabled, {@code false} otherwise. */ + @Override public boolean autoDeclarePrefixes() { return autoDeclarePrefixes; } @@ -153,6 +157,7 @@ public boolean autoDeclarePrefixes() { * * @return The {@link PrefixOrderingEnum} for prefix ordering. */ + @Override public PrefixOrderingEnum getPrefixOrdering() { return prefixOrdering; } @@ -162,6 +167,7 @@ public PrefixOrderingEnum getPrefixOrdering() { * * @return The {@link PrefixHandler} managing all prefix mappings. */ + @Override public PrefixHandler getPrefixHandler() { return prefixHandler; } @@ -216,6 +222,7 @@ public boolean useMultilineLiterals() { * * @return {@code true} if pretty-printing is enabled, {@code false} otherwise. */ + @Override public boolean prettyPrint() { return prettyPrint; } @@ -225,6 +232,7 @@ public boolean prettyPrint() { * * @return The indentation string. */ + @Override public String getIndent() { return indent; } @@ -234,6 +242,7 @@ public String getIndent() { * * @return The maximum line length. */ + @Override public int getMaxLineLength() { return maxLineLength; } @@ -252,6 +261,7 @@ public boolean groupBySubject() { * * @return {@code true} if subject sorting is enabled, {@code false} otherwise. */ + @Override public boolean sortSubjects() { return sortSubjects; } @@ -261,12 +271,13 @@ public boolean sortSubjects() { * * @return {@code true} if predicate sorting is enabled, {@code false} otherwise. */ + @Override public boolean sortPredicates() { return sortPredicates; } /** - * An abstract base builder for {@link AbstractTFamilyOption}. + * An abstract base builder for {@link AbstractTFamilyOptions}. * This builder provides methods for setting Turtle Trig serialization configuration options. * parameter (`S`) to allow concrete subclass builders to return their own specific type, * enabling fluent API chaining. @@ -493,11 +504,11 @@ public S sortPredicates(boolean sortPredicates) { } /** - * Builds and returns a new {@link AbstractTFamilyOption} instance with the current builder settings. + * Builds and returns a new {@link AbstractTFamilyOptions} instance with the current builder settings. * This method must be implemented by concrete builder subclasses to return their specific configuration type. * - * @return A new {@code AbstractTFamilyOption} instance or a subclass instance. + * @return A new {@code AbstractTFamilyOptions} instance or a subclass instance. */ - public abstract AbstractTFamilyOption build(); + public abstract AbstractTFamilyOptions build(); } } \ No newline at end of file diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializer.java index 7177757b6..1e11e8688 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializer.java @@ -15,7 +15,13 @@ import fr.inria.corese.core.next.api.Resource; import fr.inria.corese.core.next.api.Statement; import fr.inria.corese.core.next.api.base.io.RDFFormat; +import fr.inria.corese.core.next.api.io.IOOptions; +import fr.inria.corese.core.next.api.io.common.BaseIRIOptions; +import fr.inria.corese.core.next.api.io.serialization.LineEndingOptions; +import fr.inria.corese.core.next.api.io.serialization.PrettyPrintOptions; import fr.inria.corese.core.next.impl.io.serialization.base.AbstractGraphSerializer; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractSerializerOptions; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractTFamilyOptions; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; /** @@ -50,7 +56,7 @@ public class TriGSerializer extends AbstractGraphSerializer { * @throws NullPointerException if the provided model is null. */ public TriGSerializer(Model model) { - this(model, TriGSerializerOptions.defaultConfig()); + super(model, TriGSerializerOptions.defaultConfig()); } /** @@ -61,24 +67,21 @@ public TriGSerializer(Model model) { * This config object should be an instance of {@code TriGConfig} or a subclass thereof. * @throws NullPointerException if the provided model or configuration is null. */ - public TriGSerializer(Model model, TriGSerializerOptions config) { - super(model, config); + public TriGSerializer(Model model, IOOptions config) { + this(model); Objects.requireNonNull(config, "TriGConfig cannot be null"); - } - - /** - * Helper method to safely cast the generic config to TriGConfig. - * This should be called before accessing any methods specific to TriGConfig. - * - * @return The config cast to TriGConfig. - * @throws IllegalStateException if the config is not an instance of TriGConfig. - */ - private TriGSerializerOptions getTriGConfig() { - if (!(option instanceof TriGSerializerOptions)) { - throw new IllegalStateException("Current serializer configuration is not an instance of TriGConfig. " + - "TriGSerializer requires a TriGConfig instance."); + if(config instanceof AbstractSerializerOptions turtleSerializerOptions) { + this.option = turtleSerializerOptions; + } else { + TriGSerializerOptions.Builder optionBuilder = new TriGSerializerOptions.Builder(); + if(config instanceof BaseIRIOptions baseIRIOptions) { + optionBuilder.baseIRI(baseIRIOptions.getBaseIRI()); + } + if(config instanceof LineEndingOptions lineEndingOptions) { + optionBuilder.lineEnding(lineEndingOptions.getLineEnding()); + } + this.option = optionBuilder.build(); } - return (TriGSerializerOptions) option; } /** @@ -90,11 +93,9 @@ private TriGSerializerOptions getTriGConfig() { */ @Override protected void doWriteStatements(Writer writer) throws IOException { - TriGSerializerOptions trigConfig = getTriGConfig(); - - if (trigConfig.includeContext()) { + if (this.option instanceof AbstractTFamilyOptions trigConfig && trigConfig.includeContext()) { writeStatementsWithContext(writer); - } else if (trigConfig.useCompactTriples() && trigConfig.groupBySubject()) { + } else if (this.option instanceof AbstractTFamilyOptions trigConfig && trigConfig.useCompactTriples() && trigConfig.groupBySubject()) { writeOptimizedStatements(writer); } else { writeSimpleStatements(writer); @@ -110,19 +111,18 @@ protected void doWriteStatements(Writer writer) throws IOException { * @throws IOException if an I/O error occurs. */ private void writeStatementsWithContext(Writer writer) throws IOException { - TriGSerializerOptions trigConfig = getTriGConfig(); - Map> byContext = new HashMap<>(); model.stream() .filter(stmt -> !isConsumed(stmt.getSubject())) .forEach(stmt -> byContext.computeIfAbsent(stmt.getContext(), k -> new ArrayList<>()).add(stmt)); + for (Map.Entry> contextEntry : byContext.entrySet()) { Resource context = contextEntry.getKey(); List statementsInContext = contextEntry.getValue(); String initialIndent = ""; - String graphIndent = trigConfig.prettyPrint() ? trigConfig.getIndent() : ""; + String graphIndent = this.option instanceof PrettyPrintOptions prettyConfig && prettyConfig.prettyPrint() ? prettyConfig.getIndent() : ""; if (context != null) { if (context.isIRI()) { @@ -132,11 +132,13 @@ private void writeStatementsWithContext(Writer writer) throws IOException { } writer.write(SerializationConstants.SPACE); writer.write(SerializationConstants.OPEN_BRACE); - writer.write(trigConfig.getLineEnding()); + if(this.option instanceof LineEndingOptions lineEndingOptions) { + writer.write(lineEndingOptions.getLineEnding()); + } initialIndent = graphIndent; } - Map> bySubject = trigConfig.sortSubjects() + Map> bySubject = this.option instanceof PrettyPrintOptions prettyConfig && prettyConfig.sortSubjects() ? new TreeMap<>(Comparator.nullsFirst(Comparator.comparing(Resource::stringValue))) : new HashMap<>(); @@ -147,7 +149,7 @@ private void writeStatementsWithContext(Writer writer) throws IOException { writeValue(writer, subjectEntry.getKey()); writer.write(SerializationConstants.SPACE); - Map> byPredicate = trigConfig.sortPredicates() ? + Map> byPredicate = this.option instanceof PrettyPrintOptions prettyConfig && prettyConfig.sortPredicates() ? new TreeMap<>(Comparator.comparing(IRI::stringValue)) : new HashMap<>(); @@ -157,8 +159,8 @@ private void writeStatementsWithContext(Writer writer) throws IOException { for (Map.Entry> predicateEntry : byPredicate.entrySet()) { if (!firstPredicate) { writer.write(SerializationConstants.SEMICOLON); - if (trigConfig.prettyPrint()) { - writer.write(trigConfig.getLineEnding() + initialIndent + trigConfig.getIndent()); + if (this.option instanceof PrettyPrintOptions prettyConfig && this.option instanceof LineEndingOptions lineEndingOptions && prettyConfig.prettyPrint()) { + writer.write(lineEndingOptions.getLineEnding() + initialIndent + prettyConfig.getIndent()); } else { writer.write(SerializationConstants.SPACE); } @@ -172,8 +174,10 @@ private void writeStatementsWithContext(Writer writer) throws IOException { for (Statement stmt : predicateEntry.getValue()) { if (!firstObject) { writer.write(SerializationConstants.COMMA); - if (trigConfig.prettyPrint()) { - writer.write(trigConfig.getLineEnding() + initialIndent + trigConfig.getIndent() + trigConfig.getIndent()); + if (this.option instanceof PrettyPrintOptions prettyConfig + && this.option instanceof LineEndingOptions lineEndingOptions + && prettyConfig.prettyPrint()) { + writer.write(lineEndingOptions.getLineEnding() + initialIndent + prettyConfig.getIndent() + prettyConfig.getIndent()); } else { writer.write(SerializationConstants.SPACE); } @@ -183,14 +187,20 @@ private void writeStatementsWithContext(Writer writer) throws IOException { } } writer.write(SerializationConstants.SPACE + SerializationConstants.POINT); - writer.write(trigConfig.getLineEnding()); + if(this.option instanceof LineEndingOptions lineEndingOptions) { + writer.write(lineEndingOptions.getLineEnding()); + } } if (context != null) { writer.write(SerializationConstants.CLOSE_BRACE); - writer.write(trigConfig.getLineEnding()); + if(this.option instanceof LineEndingOptions lineEndingOptions) { + writer.write(lineEndingOptions.getLineEnding()); + } + } + if(this.option instanceof LineEndingOptions lineEndingOptions) { + writer.write(lineEndingOptions.getLineEnding()); } - writer.write(trigConfig.getLineEnding()); } } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializer.java index 917c4f664..112672059 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializer.java @@ -5,13 +5,17 @@ import java.util.Objects; import fr.inria.corese.core.next.api.base.io.RDFFormat; +import fr.inria.corese.core.next.api.io.IOOptions; +import fr.inria.corese.core.next.api.io.common.BaseIRIOptions; +import fr.inria.corese.core.next.api.io.serialization.LineEndingOptions; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractSerializerOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import fr.inria.corese.core.next.api.Model; import fr.inria.corese.core.next.api.Statement; import fr.inria.corese.core.next.impl.io.serialization.base.AbstractGraphSerializer; -import fr.inria.corese.core.next.impl.io.serialization.option.AbstractTFamilyOption; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractTFamilyOptions; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; /** @@ -47,7 +51,7 @@ public class TurtleSerializer extends AbstractGraphSerializer { * @throws NullPointerException if the provided model is null. */ public TurtleSerializer(Model model) { - this(model, TurtleSerializerOptions.defaultConfig()); + super(model, TurtleSerializerOptions.defaultConfig()); } /** @@ -57,9 +61,21 @@ public TurtleSerializer(Model model) { * @param config the {@link TurtleSerializerOptions} to use for serialization. Must not be null. * @throws NullPointerException if the provided model or configuration is null. */ - public TurtleSerializer(Model model, TurtleSerializerOptions config) { - super(model, config); + public TurtleSerializer(Model model, IOOptions config) { + this(model); Objects.requireNonNull(config, "TurtleConfig cannot be null"); + if(config instanceof AbstractSerializerOptions turtleSerializerOptions) { + this.option = turtleSerializerOptions; + } else { + TurtleSerializerOptions.Builder optionBuilder = new TurtleSerializerOptions.Builder(); + if(config instanceof BaseIRIOptions baseIRIOptions) { + optionBuilder.baseIRI(baseIRIOptions.getBaseIRI()); + } + if(config instanceof LineEndingOptions lineEndingOptions) { + optionBuilder.lineEnding(lineEndingOptions.getLineEnding()); + } + this.option = optionBuilder.build(); + } } /** * Retrieves the RDF format supported by this serializer, which is Turtle @@ -81,7 +97,7 @@ public RDFFormat getRDFFormat() { */ @Override protected void doWriteStatements(Writer writer) throws IOException { - AbstractTFamilyOption tFamilyConfig = (AbstractTFamilyOption) option; + AbstractTFamilyOptions tFamilyConfig = (AbstractTFamilyOptions) option; if (tFamilyConfig.useCompactTriples() && tFamilyConfig.groupBySubject()) { writeOptimizedStatements(writer); From 61b231eb179739e4601a13527aec52800b451ede Mon Sep 17 00:00:00 2001 From: Pierre Maillot Date: Tue, 9 Dec 2025 11:07:27 +0100 Subject: [PATCH 03/12] RDFXML done --- .../BlankNodeIdGenerationOptions.java | 11 +++ .../serialization/DatatypePolicyOptions.java | 14 +++ .../io/serialization/PrettyPrintOptions.java | 9 ++ .../io/serialization/UsesPrefixOptions.java | 4 +- .../option/AbstractSerializerOptions.java | 4 +- .../rdfxml/RDFXMLSerializer.java | 85 ++++++++++++------- .../rdfxml/RDFXMLSerializerOptions.java | 11 --- 7 files changed, 92 insertions(+), 46 deletions(-) create mode 100644 src/main/java/fr/inria/corese/core/next/api/io/serialization/BlankNodeIdGenerationOptions.java create mode 100644 src/main/java/fr/inria/corese/core/next/api/io/serialization/DatatypePolicyOptions.java diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/BlankNodeIdGenerationOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serialization/BlankNodeIdGenerationOptions.java new file mode 100644 index 000000000..479401450 --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/api/io/serialization/BlankNodeIdGenerationOptions.java @@ -0,0 +1,11 @@ +package fr.inria.corese.core.next.api.io.serialization; + +public interface BlankNodeIdGenerationOptions { + + /** + * Checks if deterministic blank node IDs should be generated. + * + * @return {@code true} if stable blank node IDs are enabled, {@code false} otherwise. + */ + boolean stableBlankNodeIds(); +} diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/DatatypePolicyOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serialization/DatatypePolicyOptions.java new file mode 100644 index 000000000..1abab55e3 --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/api/io/serialization/DatatypePolicyOptions.java @@ -0,0 +1,14 @@ +package fr.inria.corese.core.next.api.io.serialization; + +import fr.inria.corese.core.next.impl.io.serialization.option.LiteralDatatypePolicyEnum; + +public interface DatatypePolicyOptions { + + + /** + * Returns the policy for how literal datatypes are printed. + * + * @return The {@link LiteralDatatypePolicyEnum} indicating the literal datatype serialization policy. + */ + LiteralDatatypePolicyEnum getLiteralDatatypePolicy(); +} diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/PrettyPrintOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serialization/PrettyPrintOptions.java index b7ab0c79b..f26935083 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serialization/PrettyPrintOptions.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serialization/PrettyPrintOptions.java @@ -1,5 +1,7 @@ package fr.inria.corese.core.next.api.io.serialization; +import fr.inria.corese.core.next.impl.io.serialization.option.PrefixOrderingEnum; + public interface PrettyPrintOptions { /** @@ -36,4 +38,11 @@ public interface PrettyPrintOptions { * @return {@code true} if predicate sorting is enabled, {@code false} otherwise. */ boolean sortPredicates(); + + /** + * Returns the policy for ordering prefix declarations. + * + * @return The {@link PrefixOrderingEnum} for prefix ordering. + */ + PrefixOrderingEnum getPrefixOrdering(); } diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/UsesPrefixOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serialization/UsesPrefixOptions.java index 7ccbbb3b7..c7723b31d 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serialization/UsesPrefixOptions.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serialization/UsesPrefixOptions.java @@ -22,11 +22,11 @@ public interface UsesPrefixOptions { * * @return The {@link PrefixOrderingEnum} for prefix ordering. */ - public PrefixOrderingEnum getPrefixOrdering(); + PrefixOrderingEnum getPrefixOrdering(); /** * Returns an unmodifiable map of custom URI prefixes. * * @return The {@link PrefixHandler} managing all prefix mappings. */ - public PrefixHandler getPrefixHandler(); + PrefixHandler getPrefixHandler(); } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java index c1da6edab..4a86244fc 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java @@ -2,6 +2,8 @@ import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.api.io.common.BaseIRIOptions; +import fr.inria.corese.core.next.api.io.serialization.BlankNodeIdGenerationOptions; +import fr.inria.corese.core.next.api.io.serialization.DatatypePolicyOptions; import fr.inria.corese.core.next.api.io.serialization.LineEndingOptions; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; @@ -16,7 +18,7 @@ * nested {@link AbstractBuilder}. Subclasses are expected to extend this * configuration and its builder to add format-specific options.

*/ -public abstract class AbstractSerializerOptions implements IOOptions, BaseIRIOptions, LineEndingOptions { +public abstract class AbstractSerializerOptions implements IOOptions, BaseIRIOptions, LineEndingOptions, BlankNodeIdGenerationOptions, DatatypePolicyOptions { /** * The policy for how literal datatypes are printed. diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java index 5e794fa24..f97b2afdb 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java @@ -16,6 +16,8 @@ import java.util.stream.Collectors; import fr.inria.corese.core.next.api.base.io.RDFFormat; +import fr.inria.corese.core.next.api.io.IOOptions; +import fr.inria.corese.core.next.api.io.serialization.*; import fr.inria.corese.core.next.impl.common.vocabulary.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,7 +28,6 @@ import fr.inria.corese.core.next.api.Resource; import fr.inria.corese.core.next.api.Statement; import fr.inria.corese.core.next.api.Value; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; import fr.inria.corese.core.next.impl.exception.SerializationException; import fr.inria.corese.core.next.impl.io.serialization.option.LiteralDatatypePolicyEnum; import fr.inria.corese.core.next.impl.io.serialization.option.PrefixOrderingEnum; @@ -53,7 +54,7 @@ public class RDFXMLSerializer implements RDFSerializer { private static final Logger logger = LoggerFactory.getLogger(RDFXMLSerializer.class); private final Model model; - private final RDFXMLSerializerOption config; + private final IOOptions config; private final Map iriToPrefixMapping; private final Map prefixToIriMapping; private final Map blankNodeIds; @@ -62,23 +63,23 @@ public class RDFXMLSerializer implements RDFSerializer { /** * Constructs a new {@code XmlSerializer} instance with the specified model and default configuration. - * The default configuration is obtained from {@link RDFXMLSerializerOption#defaultConfig()}. + * The default configuration is obtained from {@link RDFXMLSerializerOptions#defaultConfig()}. * * @param model the {@link Model} to serialize. Must not be null. * @throws NullPointerException if the provided model is null. */ public RDFXMLSerializer(Model model) { - this(model, RDFXMLSerializerOption.defaultConfig()); + this(model, RDFXMLSerializerOptions.defaultConfig()); } /** * Constructs a new {@code XmlSerializer} instance with the specified model and custom configuration. * * @param model the {@link Model} to serialize. Must not be null. - * @param config the {@link RDFXMLSerializerOption} to use for serialization. Must not be null. + * @param config the {@link RDFXMLSerializerOptions} to use for serialization. Must not be null. * @throws NullPointerException if the provided model or configuration is null. */ - public RDFXMLSerializer(Model model, RDFXMLSerializerOption config) { + public RDFXMLSerializer(Model model, IOOptions config) { this.model = Objects.requireNonNull(model, "Model cannot be null"); this.config = Objects.requireNonNull(config, "Configuration cannot be null"); this.iriToPrefixMapping = new HashMap<>(); @@ -92,8 +93,8 @@ public RDFXMLSerializer(Model model, RDFXMLSerializerOption config) { * The custom prefixes map in XmlConfig is expected to be {prefix: namespaceURI}. */ private void initializePrefixes() { - if (config.usePrefixes()) { - for (Map.Entry entry : config.getCustomPrefixes().entrySet()) { + if (this.config instanceof UsesPrefixOptions usesPrefixOptions && usesPrefixOptions.usePrefixes()) { + for (Map.Entry entry : usesPrefixOptions.getPrefixHandler().getPrefixMap().entrySet()) { addPrefixMapping(entry.getValue(), entry.getKey()); } } @@ -132,7 +133,9 @@ public RDFFormat getRDFFormat() { */ private void writeXmlDeclaration(Writer writer) throws IOException { writer.write(SerializationConstants.XML_DECLARATION_START); - writer.write(config.getLineEnding()); + if(this.config instanceof LineEndingOptions lineEndingOptions) { + writer.write(lineEndingOptions.getLineEnding()); + } } /** @@ -143,30 +146,39 @@ private void writeXmlDeclaration(Writer writer) throws IOException { * @throws IOException if an I/O error occurs. */ private void writeRdfRootElement(Writer writer) throws IOException { - if (config.usePrefixes() && config.autoDeclarePrefixes()) { + if (this.config instanceof UsesPrefixOptions usesPrefixOptions && usesPrefixOptions.usePrefixes() && usesPrefixOptions.autoDeclarePrefixes()) { collectUsedNamespaces(); } writer.write(SerializationConstants.RDF_ROOT_START); writeNamespaceAttributes(writer); writer.write(">"); - writer.write(config.getLineEnding()); + if(this.config instanceof LineEndingOptions lineEndingOptions) { + writer.write(lineEndingOptions.getLineEnding()); + } Map> statementsBySubject = cachedStatements.stream() .collect(Collectors.groupingBy(Statement::getSubject)); List sortedSubjects = new ArrayList<>(statementsBySubject.keySet()); - if (config.sortSubjects()) { + if (this.config instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.sortSubjects()) { Collections.sort(sortedSubjects, Comparator.comparing(Value::stringValue)); } + String zeroIndent = ""; for (Resource subject : sortedSubjects) { - writeDescriptionElement(writer, subject, statementsBySubject.get(subject), config.getIndent()); + if(this.config instanceof PrettyPrintOptions prettyPrintOptions) { + writeDescriptionElement(writer, subject, statementsBySubject.get(subject), prettyPrintOptions.getIndent()); + } else { + writeDescriptionElement(writer, subject, statementsBySubject.get(subject), zeroIndent); + } } writer.write(SerializationConstants.RDF_ROOT_END); - writer.write(config.getLineEnding()); + if(this.config instanceof LineEndingOptions lineEndingOptions) { + writer.write(lineEndingOptions.getLineEnding()); + } } /** @@ -181,7 +193,7 @@ private void writeNamespaceAttributes(Writer writer) throws IOException { } List prefixes = new ArrayList<>(prefixToIriMapping.keySet()); - if (config.getPrefixOrdering() == PrefixOrderingEnum.ALPHABETICAL) { + if (this.config instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.getPrefixOrdering() == PrefixOrderingEnum.ALPHABETICAL) { Collections.sort(prefixes); } @@ -319,7 +331,10 @@ private String generateUniquePrefix(String basePrefix) { * @throws IOException if an I/O error occurs. */ private void writeDescriptionElement(Writer writer, Resource subject, List statements, String currentIndent) throws IOException { - String nextIndent = currentIndent + config.getIndent(); + String nextIndent = currentIndent; + if(this.config instanceof PrettyPrintOptions prettyPrintOptions) { + nextIndent = currentIndent + prettyPrintOptions.getIndent(); + } writer.write(currentIndent); if (subject.isIRI()) { @@ -327,13 +342,15 @@ private void writeDescriptionElement(Writer writer, Resource subject, List", SerializationConstants.RDF_DESCRIPTION_START, SerializationConstants.RDF_NODEID_ATTRIBUTE, getBlankNodeId(subject))); } - writer.write(config.getLineEnding()); + if(this.config instanceof LineEndingOptions lineEndingOptions) { + writer.write(lineEndingOptions.getLineEnding()); + } Map> statementsByPredicate = statements.stream() .collect(Collectors.groupingBy(Statement::getPredicate)); List sortedPredicates = new ArrayList<>(statementsByPredicate.keySet()); - if (config.sortPredicates()) { + if (this.config instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.sortPredicates()) { Collections.sort(sortedPredicates, Comparator.comparing(Value::stringValue)); } @@ -345,7 +362,9 @@ private void writeDescriptionElement(Writer writer, Resource subject, List", SerializationConstants.RDF_RESOURCE_ATTRIBUTE, escapeXmlAttribute(object.stringValue()))); - writer.write(config.getLineEnding()); + if(this.config instanceof LineEndingOptions lineEndingOptions) { + writer.write(lineEndingOptions.getLineEnding()); + } } else if (object.isBNode()) { writer.write(String.format(" %s=\"%s\"/>", SerializationConstants.RDF_NODEID_ATTRIBUTE, getBlankNodeId((Resource) object))); - writer.write(config.getLineEnding()); + if(this.config instanceof LineEndingOptions lineEndingOptions) { + writer.write(lineEndingOptions.getLineEnding()); + } } else if (object.isLiteral()) { Literal literal = (Literal) object; @@ -397,15 +420,12 @@ private void writePropertyElement(Writer writer, IRI predicate, Value object, St writer.write(">"); } - if (config.useMultilineLiterals() && (literal.stringValue().contains(SerializationConstants.LINE_FEED) || literal.stringValue().contains(SerializationConstants.CARRIAGE_RETURN))) { - - writer.write(escapeXmlContent(literal.stringValue())); - } else { - writer.write(escapeXmlContent(literal.stringValue())); - } + writer.write(escapeXmlContent(literal.stringValue())); writer.write(String.format("", elementName)); - writer.write(config.getLineEnding()); + if(this.config instanceof LineEndingOptions lineEndingOptions) { + writer.write(lineEndingOptions.getLineEnding()); + } } else { throw new IllegalArgumentException("Unsupported value type for RDF/XML serialization: " + object.getClass().getName()); } @@ -419,7 +439,7 @@ private void writePropertyElement(Writer writer, IRI predicate, Value object, St */ private String getBlankNodeId(Resource bNode) { return blankNodeIds.computeIfAbsent(bNode, k -> { - if (config.stableBlankNodeIds()) { + if (this.config instanceof BlankNodeIdGenerationOptions bnGenOptions && bnGenOptions.stableBlankNodeIds()) { return "b" + (blankNodeCounter++); } else { return bNode.stringValue().substring(2); @@ -443,9 +463,10 @@ private boolean shouldWriteDatatype(Literal literal) { return false; } - return config.getLiteralDatatypePolicy() == LiteralDatatypePolicyEnum.ALWAYS_TYPED || - (!datatype.equals(XSD.xsdString.getIRI()) && - config.getLiteralDatatypePolicy() == LiteralDatatypePolicyEnum.MINIMAL); + return config instanceof DatatypePolicyOptions datatypePolicyOptions + && (datatypePolicyOptions.getLiteralDatatypePolicy() == LiteralDatatypePolicyEnum.ALWAYS_TYPED + || (!datatype.equals(XSD.xsdString.getIRI()) + && datatypePolicyOptions.getLiteralDatatypePolicy() == LiteralDatatypePolicyEnum.MINIMAL)); } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOptions.java index eb4a03fd0..9c2f00231 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOptions.java @@ -129,17 +129,6 @@ public PrefixHandler getPrefixHandler() { return prefixHandler; } - /** - * Returns an unmodifiable map of custom URI prefixes for backward compatibility. - * - * @return A map where keys are prefix names and values are namespace URIs. - * @deprecated Use {@link #getPrefixHandler()} instead for full prefix management capabilities. - */ - @Deprecated - public Map getCustomPrefixes() { - return prefixHandler.getPrefixMap(); - } - /** * Checks if human-readable formatting (pretty-printing) is enabled. * From 3022d9b6ed6788c7ec3fd591527d5905a99cb0e4 Mon Sep 17 00:00:00 2001 From: Pierre Maillot Date: Tue, 9 Dec 2025 11:20:24 +0100 Subject: [PATCH 04/12] NQuads NTriples done --- .../base/AbstractLineBasedSerializer.java | 2 +- .../nquads/NQuadsSerializer.java | 27 ++++++++++++++++--- .../ntriples/NTriplesSerializer.java | 27 ++++++++++++++++--- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractLineBasedSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractLineBasedSerializer.java index 061baf8e8..a998a7975 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractLineBasedSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractLineBasedSerializer.java @@ -37,7 +37,7 @@ public abstract class AbstractLineBasedSerializer implements RDFSerializer { private static final Logger logger = LoggerFactory.getLogger(AbstractLineBasedSerializer.class); protected final Model model; - protected final AbstractSerializerOptions config; + protected AbstractSerializerOptions config; /** * Constructs a new line-based serializer. diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/nquads/NQuadsSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/nquads/NQuadsSerializer.java index 5de866837..fe99a41b9 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/nquads/NQuadsSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/nquads/NQuadsSerializer.java @@ -4,7 +4,13 @@ import fr.inria.corese.core.next.api.Resource; import fr.inria.corese.core.next.api.Statement; import fr.inria.corese.core.next.api.base.io.RDFFormat; +import fr.inria.corese.core.next.api.io.IOOptions; +import fr.inria.corese.core.next.api.io.common.BaseIRIOptions; +import fr.inria.corese.core.next.api.io.serialization.BlankNodeIdGenerationOptions; +import fr.inria.corese.core.next.api.io.serialization.LineEndingOptions; import fr.inria.corese.core.next.impl.io.serialization.base.AbstractLineBasedSerializer; +import fr.inria.corese.core.next.impl.io.serialization.ntriples.NTriplesSerializerOptions; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractNFamilyOptions; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +39,7 @@ public class NQuadsSerializer extends AbstractLineBasedSerializer { * @throws NullPointerException if the provided model is null. */ public NQuadsSerializer(Model model) { - this(model, NQuadsSerializerOptions.defaultConfig()); + super(model, NQuadsSerializerOptions.defaultConfig()); } /** @@ -44,9 +50,24 @@ public NQuadsSerializer(Model model) { * This config object should be an instance of {@code NQuadsConfig} or a subclass thereof. * @throws NullPointerException if the provided model or config is null. */ - public NQuadsSerializer(Model model, NQuadsSerializerOptions config) { - super(model, config); + public NQuadsSerializer(Model model, IOOptions config) { + this(model); Objects.requireNonNull(config, "NQuadsConfig cannot be null"); + if(config instanceof AbstractNFamilyOptions nFamilyOptions) { + this.config = nFamilyOptions; + } else { + NTriplesSerializerOptions.Builder optionBuilder = new NTriplesSerializerOptions.Builder(); + if(config instanceof BaseIRIOptions baseIRIOptions) { + optionBuilder.baseIRI(baseIRIOptions.getBaseIRI()); + } + if(config instanceof LineEndingOptions lineEndingOptions) { + optionBuilder.lineEnding(lineEndingOptions.getLineEnding()); + } + if(config instanceof BlankNodeIdGenerationOptions blankNodeIdGenerationOptions) { + optionBuilder.stableBlankNodeIds(blankNodeIdGenerationOptions.stableBlankNodeIds()); + } + this.config = optionBuilder.build(); + } } /** diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/ntriples/NTriplesSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/ntriples/NTriplesSerializer.java index d4b1370e7..45a318800 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/ntriples/NTriplesSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/ntriples/NTriplesSerializer.java @@ -5,6 +5,12 @@ import java.util.Objects; import fr.inria.corese.core.next.api.base.io.RDFFormat; +import fr.inria.corese.core.next.api.io.IOOptions; +import fr.inria.corese.core.next.api.io.common.BaseIRIOptions; +import fr.inria.corese.core.next.api.io.serialization.BlankNodeIdGenerationOptions; +import fr.inria.corese.core.next.api.io.serialization.DatatypePolicyOptions; +import fr.inria.corese.core.next.api.io.serialization.LineEndingOptions; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractNFamilyOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +39,7 @@ public class NTriplesSerializer extends AbstractLineBasedSerializer { * @throws NullPointerException if the provided model is null. */ public NTriplesSerializer(Model model) { - this(model, NTriplesSerializerOptions.defaultConfig()); + super(model, NTriplesSerializerOptions.defaultConfig()); } /** @@ -44,9 +50,24 @@ public NTriplesSerializer(Model model) { * This config object should be an instance of {@code NTriplesConfig} or a subclass thereof. * @throws NullPointerException if the provided model or config is null. */ - public NTriplesSerializer(Model model, NTriplesSerializerOptions config) { - super(model, config); + public NTriplesSerializer(Model model, IOOptions config) { + this(model); Objects.requireNonNull(config, "NTriplesConfig cannot be null"); + if(config instanceof AbstractNFamilyOptions nFamilyOptions) { + this.config = nFamilyOptions; + } else { + NTriplesSerializerOptions.Builder optionBuilder = new NTriplesSerializerOptions.Builder(); + if(config instanceof BaseIRIOptions baseIRIOptions) { + optionBuilder.baseIRI(baseIRIOptions.getBaseIRI()); + } + if(config instanceof LineEndingOptions lineEndingOptions) { + optionBuilder.lineEnding(lineEndingOptions.getLineEnding()); + } + if(config instanceof BlankNodeIdGenerationOptions blankNodeIdGenerationOptions) { + optionBuilder.stableBlankNodeIds(blankNodeIdGenerationOptions.stableBlankNodeIds()); + } + this.config = optionBuilder.build(); + } } /** * Retrieves the RDF format supported by this serializer, which is N-TRIPLES. From 24b013cd4462e76fa3b6d5f6299f9932ce58113b Mon Sep 17 00:00:00 2001 From: Pierre Maillot Date: Wed, 10 Dec 2025 15:42:53 +0100 Subject: [PATCH 05/12] Turtle and Trig fixed --- .../corese/core/next/api/IPrefixHandler.java | 8 + .../impl/common/prefix/PrefixHandler.java | 25 ++- .../base/AbstractGraphSerializer.java | 145 ++++++++---------- .../option/AbstractSerializerOptions.java | 25 +++ .../option/AbstractTFamilyOptions.java | 31 ++++ .../io/serialization/trig/TriGSerializer.java | 18 +-- .../turtle/TurtleSerializer.java | 20 +-- .../turtle/TurtleSerializerOptions.java | 19 +++ .../io/parser/rdfxml/RDFXMLCircularTest.java | 2 +- .../trig/TriGSerializerTest.java | 33 +--- .../turtle/TurtleSerializerTest.java | 39 +---- 11 files changed, 180 insertions(+), 185 deletions(-) diff --git a/src/main/java/fr/inria/corese/core/next/api/IPrefixHandler.java b/src/main/java/fr/inria/corese/core/next/api/IPrefixHandler.java index 8a205fba5..29742fee9 100644 --- a/src/main/java/fr/inria/corese/core/next/api/IPrefixHandler.java +++ b/src/main/java/fr/inria/corese/core/next/api/IPrefixHandler.java @@ -37,6 +37,14 @@ public interface IPrefixHandler { */ boolean hasPrefix(String prefix); + /** + * Checks if a namespace has a prefix + * + * @param namespace the namespace to check + * @return true if the namespace exists in mappings, false otherwise + */ + boolean hasNamespace(String namespace); + /** * Returns all registered prefixes. * Order of iteration is implementation-dependent but should be consistent diff --git a/src/main/java/fr/inria/corese/core/next/impl/common/prefix/PrefixHandler.java b/src/main/java/fr/inria/corese/core/next/impl/common/prefix/PrefixHandler.java index 321ede195..0403836a8 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/common/prefix/PrefixHandler.java +++ b/src/main/java/fr/inria/corese/core/next/impl/common/prefix/PrefixHandler.java @@ -45,6 +45,12 @@ public PrefixHandler(boolean includeStandardVocabularies) { } } + public PrefixHandler(PrefixHandler oHandler) { + this.prefixToNamespace = new ConcurrentHashMap<>(oHandler.prefixToNamespace); + this.namespaceToPrefix = new ConcurrentHashMap<>(oHandler.namespaceToPrefix); + this.defaultNamespace = oHandler.defaultNamespace; + } + /** * Initializes the handler with standard W3C vocabulary prefixes by using the * dedicated Vocabulary enum classes. @@ -57,14 +63,11 @@ private void initializeStandardVocabularies() { OWL.class, FOAF.class ); - - for (Class> vocabClass : vocabularyClasses) { - Enum[] constants = vocabClass.getEnumConstants(); - if (constants.length > 0) { - Vocabulary vocabInstance = (Vocabulary) constants[0]; - setPrefix(vocabInstance.getPreferredPrefix(), vocabInstance.getNamespace()); - } - } + setPrefix(RDF.getVocabularyPreferredPrefix(), RDF.getVocabularyNamespace()); + setPrefix(RDFS.getVocabularyPreferredPrefix(), RDFS.getVocabularyNamespace()); + setPrefix(XSD.getVocabularyPreferredPrefix(), XSD.getVocabularyNamespace()); + setPrefix(OWL.getVocabularyPreferredPrefix(), OWL.getVocabularyNamespace()); + setPrefix(FOAF.getVocabularyPreferredPrefix(), FOAF.getVocabularyNamespace()); } /** @@ -89,6 +92,7 @@ public void setPrefix(String prefix, String namespace) { String oldNamespace = prefixToNamespace.get(prefix); if (oldNamespace != null && !oldNamespace.equals(namespace)) { namespaceToPrefix.remove(oldNamespace); + prefixToNamespace.remove(prefix); } prefixToNamespace.put(prefix, namespace); @@ -142,6 +146,11 @@ public boolean hasPrefix(String prefix) { return prefixToNamespace.containsKey(prefix); } + @Override + public boolean hasNamespace(String namespace) { + return namespaceToPrefix.containsKey(namespace); + } + /** * Gets the default namespace * diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java index 356e877dc..9a0becb38 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java @@ -1,13 +1,16 @@ package fr.inria.corese.core.next.impl.io.serialization.base; import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.api.io.serialization.PrettyPrintOptions; import fr.inria.corese.core.next.api.io.serialization.UsesPrefixOptions; import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; +import fr.inria.corese.core.next.impl.common.util.IRIUtils; import fr.inria.corese.core.next.impl.common.vocabulary.*; import fr.inria.corese.core.next.impl.exception.SerializationException; import fr.inria.corese.core.next.impl.io.serialization.option.*; +import fr.inria.corese.core.next.impl.io.serialization.turtle.TurtleSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,8 +46,7 @@ public abstract class AbstractGraphSerializer implements RDFSerializer { protected final Model model; protected AbstractSerializerOptions option; - protected final Map iriToPrefixMapping; - protected final Map prefixToIriMapping; + protected PrefixHandler prefixHandler; protected final Set consumedBlankNodes; protected final Set currentlyWritingBlankNodes; @@ -52,33 +54,24 @@ public abstract class AbstractGraphSerializer implements RDFSerializer { * Constructs a new abstract TriG/Turtle serializer instance. * * @param model the {@link Model} to serialize. Must not be null. - * @param option the {@link AbstractSerializerOptions} to use for serialization. Must not be null. + * @param config the {@link AbstractSerializerOptions} to use for serialization. Must not be null. * @throws NullPointerException if the provided model or configuration is null. */ - protected AbstractGraphSerializer(Model model, AbstractSerializerOptions option) { + protected AbstractGraphSerializer(Model model, IOOptions config) { this.model = Objects.requireNonNull(model, "The model cannot be null"); - this.option = Objects.requireNonNull(option, "The configuration cannot be null"); - this.iriToPrefixMapping = new HashMap<>(); - this.prefixToIriMapping = new HashMap<>(); + Objects.requireNonNull(config, "The configuration cannot be null"); + if(config instanceof AbstractSerializerOptions abstractSerializerOptions) { + this.option = abstractSerializerOptions; + } else { + throw new IllegalArgumentException("AbstractGraphSerializer expect option object to extend AbstractSerializerOptions. Inheritor class should have taken care of that."); + } + if(config instanceof UsesPrefixOptions usesPrefixOptions) { + this.prefixHandler = usesPrefixOptions.getPrefixHandler(); + } else { + this.prefixHandler = new PrefixHandler(false); + } this.consumedBlankNodes = new HashSet<>(); this.currentlyWritingBlankNodes = new HashSet<>(); - initializePrefixes(); - } - - /** - * Initializes prefix mappings by adding custom prefixes from the configuration. - */ - private void initializePrefixes() { - if (option instanceof UsesPrefixOptions prefixOptions && prefixOptions.usePrefixes()) { - PrefixHandler prefixHandler = prefixOptions.getPrefixHandler(); - - for (String prefix : prefixHandler.getPrefixes()) { - String namespace = prefixHandler.getNamespace(prefix); - if (namespace != null) { - addPrefixMapping(namespace, prefix); - } - } - } } /** @@ -128,21 +121,21 @@ protected void writeHeader(Writer writer) throws IOException { option.getLineEnding())); } + Set actuallyUsedNamespaces = Set.of(); if (option instanceof UsesPrefixOptions prefixOptions && prefixOptions.usePrefixes() && prefixOptions.autoDeclarePrefixes()) { - collectUsedNamespaces(); + actuallyUsedNamespaces = collectUsedNamespaces(); } - writePrefixDeclarations(writer); + writePrefixDeclarations(writer, actuallyUsedNamespaces); } /** * Collects all namespaces used in the model and attempts to assign prefixes to them * if auto-declaration is enabled and they are not already mapped. */ - protected void collectUsedNamespaces() { - + protected Set collectUsedNamespaces() { Set namespaces = model.stream() .flatMap(stmt -> { List values = new ArrayList<>(Arrays.asList( @@ -153,21 +146,27 @@ protected void collectUsedNamespaces() { if (stmt.getContext() != null) { values.add(stmt.getContext()); } + if(stmt.getObject().isLiteral() + && ((Literal) stmt.getObject()).getDatatype() != null) { + values.add(((Literal) stmt.getObject()).getDatatype()); + } return values.stream(); }) .filter(Objects::nonNull) .filter(Value::isIRI) - .map(v -> getNamespace(v.stringValue())) + .map(v -> IRIUtils.guessNamespace(v.stringValue())) .collect(Collectors.toSet()); namespaces.forEach(namespace -> { - if (!iriToPrefixMapping.containsKey(namespace)) { + if (!this.prefixHandler.hasNamespace(namespace)) { String prefix = getSuggestedPrefix(namespace); if (prefix != null) { addPrefixMapping(namespace, prefix); } } }); + + return namespaces; } /** @@ -176,9 +175,10 @@ protected void collectUsedNamespaces() { * @param writer the {@link Writer} to which prefixes will be written. * @throws IOException if an I/O error occurs. */ - protected void writePrefixDeclarations(Writer writer) throws IOException { - if(this.option instanceof UsesPrefixOptions prefixOptions) { - List prefixes = new ArrayList<>(prefixToIriMapping.keySet()); + protected void writePrefixDeclarations(Writer writer, Set actuallyUsedNamespaces) throws IOException { + if(this.option instanceof UsesPrefixOptions prefixOptions + && prefixOptions.usePrefixes()) { + List prefixes = new ArrayList<>(actuallyUsedNamespaces.stream().map(namespace -> this.prefixHandler.getPrefix(namespace)).toList()); if (prefixOptions.getPrefixOrdering() == PrefixOrderingEnum.ALPHABETICAL) { Collections.sort(prefixes); @@ -187,7 +187,7 @@ protected void writePrefixDeclarations(Writer writer) throws IOException { for (String prefix : prefixes) { writer.write(String.format("@prefix %s: <%s> .%s", prefix, - prefixToIriMapping.get(prefix), + this.prefixHandler.getNamespace(prefix), option.getLineEnding())); } @@ -197,6 +197,16 @@ protected void writePrefixDeclarations(Writer writer) throws IOException { } } + /** + * Writes prefix declarations to the writer, sorted if configured. + * + * @param writer the {@link Writer} to which prefixes will be written. + * @throws IOException if an I/O error occurs. + */ + protected void writePrefixDeclarations(Writer writer) throws IOException { + writePrefixDeclarations(writer, Set.of()); + } + /** * Serializes the model's statements in a simple manner, one per line, without grouping. * Triples already "consumed" by inline serialization are ignored. @@ -222,7 +232,10 @@ protected void writeSimpleStatements(Writer writer) throws IOException { * @throws IOException if an I/O error occurs. */ protected void writeStatement(Writer writer, Statement stmt) throws IOException { - String indent = this.option instanceof PrettyPrintOptions prettyOptions && prettyOptions.prettyPrint() ? prettyOptions.getIndent() : SerializationConstants.EMPTY_STRING; + String indent = this.option instanceof PrettyPrintOptions prettyOptions + && prettyOptions.prettyPrint() + ? prettyOptions.getIndent() + : SerializationConstants.EMPTY_STRING; writer.write(indent); // Subject @@ -248,7 +261,9 @@ protected void writeStatement(Writer writer, Statement stmt) throws IOException * @throws IOException if an I/O error occurs. */ protected void writePredicate(Writer writer, Value predicate) throws IOException { - if (this.option instanceof AbstractTFamilyOptions tFamilyOptions && tFamilyOptions.useRdfTypeShortcut() && predicate.equals(RDF.type.getIRI())) { + if (this.option instanceof AbstractTFamilyOptions tFamilyOptions + && tFamilyOptions.useRdfTypeShortcut() + && predicate.equals(RDF.type.getIRI())) { writer.write(SerializationConstants.RDF_TYPE_SHORTCUT); } else { writeValue(writer, predicate); @@ -729,17 +744,17 @@ protected boolean isRDFListHead(Resource bNode) { * @param prefix The associated prefix. */ protected void addPrefixMapping(String namespaceURI, String prefix) { - if (iriToPrefixMapping.containsKey(namespaceURI)) { - if (logger.isWarnEnabled() && !iriToPrefixMapping.get(namespaceURI).equals(prefix)) { + if (this.prefixHandler.hasNamespace(namespaceURI)) { + if (logger.isWarnEnabled() && !this.prefixHandler.getPrefix(namespaceURI).equals(prefix)) { logger.warn("Namespace URI '{}' is already mapped to prefix '{}'. Cannot map to new prefix '{}'.", - namespaceURI, iriToPrefixMapping.get(namespaceURI), prefix); + namespaceURI, this.prefixHandler.getPrefix(namespaceURI), prefix); } return; } - if (prefixToIriMapping.containsKey(prefix)) { - if (logger.isWarnEnabled() && !prefixToIriMapping.get(prefix).equals(namespaceURI)) { - String originalNamespace = prefixToIriMapping.get(prefix); + if (this.prefixHandler.hasPrefix(prefix)) { + if (logger.isWarnEnabled() && !this.prefixHandler.getNamespace(prefix).equals(namespaceURI)) { + String originalNamespace = this.prefixHandler.getNamespace(prefix); logger.warn("Prefix '{}' is already mapped to namespace '{}'. Cannot map to new namespace '{}'. " + "A new unique prefix will be generated for '{}'.", prefix, originalNamespace, namespaceURI, namespaceURI); @@ -747,31 +762,7 @@ protected void addPrefixMapping(String namespaceURI, String prefix) { return; } - iriToPrefixMapping.put(namespaceURI, prefix); - prefixToIriMapping.put(prefix, namespaceURI); - } - - /** - * Extracts the namespace URI part from an IRI string. - * This is a common heuristic for RDF IRIs. - * - * @param iriString The full IRI. - * @return The namespace URI part. - */ - protected String getNamespace(String iriString) { - int hashIdx = iriString.lastIndexOf(SerializationConstants.HASH); - int slashIdx = iriString.lastIndexOf(SerializationConstants.SLASH); - - if (hashIdx > -1) { - return iriString.substring(0, hashIdx + 1); - } else if (slashIdx > -1 && slashIdx < iriString.length() - 1) { - int dotIdx = iriString.lastIndexOf(SerializationConstants.POINT); - if (dotIdx > slashIdx) { - return iriString.substring(0, slashIdx + 1); - } - return iriString.substring(0, slashIdx + 1); - } - return iriString; + this.prefixHandler.setPrefix(prefix, namespaceURI); } /** @@ -781,11 +772,9 @@ protected String getNamespace(String iriString) { * @return The prefixed name (e.g., "ex:someResource") or null if no suitable prefix is found. */ protected String getPrefixedName(String iriString) { - for (Map.Entry entry : iriToPrefixMapping.entrySet()) { - String namespace = entry.getKey(); - String prefix = entry.getValue(); - + for (String namespace : this.prefixHandler.getNamespaces()) { if (iriString.startsWith(namespace)) { + String prefix = this.prefixHandler.getPrefix(namespace); String localName = iriString.substring(namespace.length()); if (localName.isEmpty()) { if (!prefix.isEmpty()) { @@ -808,12 +797,6 @@ protected String getPrefixedName(String iriString) { * @return A suggested prefix, or null if suggestion is not possible. */ protected String getSuggestedPrefix(String namespace) { - if (namespace.equals(RDF.getVocabularyNamespace())) return RDF.getVocabularyPreferredPrefix(); - if (namespace.equals(RDFS.getVocabularyNamespace())) return RDFS.getVocabularyPreferredPrefix(); - if (namespace.equals(XSD.getVocabularyNamespace())) return XSD.getVocabularyPreferredPrefix(); - if (namespace.equals(OWL.getVocabularyNamespace())) return OWL.getVocabularyPreferredPrefix(); - if (namespace.equals(FOAF.getVocabularyNamespace())) return FOAF.getVocabularyPreferredPrefix(); - String base = namespace; if (base.endsWith(SerializationConstants.HASH) || base.endsWith(SerializationConstants.SLASH)) { base = base.substring(0, base.length() - 1); @@ -838,13 +821,13 @@ protected String getSuggestedPrefix(String namespace) { base = base.replaceAll("[^a-zA-Z0-9]", SerializationConstants.EMPTY_STRING).toLowerCase(); if (base.isEmpty()) base = "p"; - String candidate = base; + String candidatePrefix = base; int i = 0; - while (prefixToIriMapping.containsKey(candidate) && !prefixToIriMapping.get(candidate).equals(namespace)) { - candidate = base + (++i); + while (this.prefixHandler.hasPrefix(candidatePrefix) && !this.prefixHandler.getNamespace(candidatePrefix).equals(namespace)) { + candidatePrefix = base + (++i); } - return candidate; + return candidatePrefix; } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java index 4a86244fc..73def386e 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java @@ -193,6 +193,31 @@ public abstract static class AbstractBuilder> { protected boolean validateURIs = true; protected boolean includeContext = false; + protected AbstractBuilder(IOOptions otherOptions) { + if(otherOptions instanceof AbstractSerializerOptions abstractSerializerOptions) { + this.escapeUnicode(abstractSerializerOptions.escapeUnicode()); + this.trailingDot(abstractSerializerOptions.trailingDot()); + this.strictMode(abstractSerializerOptions.isStrictMode()); + this.validateURIs(abstractSerializerOptions.validateURIs()); + this.includeContext(abstractSerializerOptions.includeContext()); + } + if(otherOptions instanceof BaseIRIOptions baseIRIOptions) { + this.baseIRI(baseIRIOptions.getBaseIRI()); + } + if(otherOptions instanceof LineEndingOptions lineEndingOptions) { + this.lineEnding(lineEndingOptions.getLineEnding()); + } + if(otherOptions instanceof BlankNodeIdGenerationOptions blankNodeIdGenerationOptions) { + this.stableBlankNodeIds(blankNodeIdGenerationOptions.stableBlankNodeIds()); + } + if(otherOptions instanceof DatatypePolicyOptions datatypePolicyOptions) { + this.literalDatatypePolicy(datatypePolicyOptions.getLiteralDatatypePolicy()); + } + } + + protected AbstractBuilder() { + } + /** * Sets the policy for how literal datatypes are printed. * diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java index c2be59d44..66d47d0f6 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java @@ -1,9 +1,12 @@ package fr.inria.corese.core.next.impl.io.serialization.option; +import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.api.io.serialization.PrettyPrintOptions; import fr.inria.corese.core.next.api.io.serialization.UsesPrefixOptions; import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Map; import java.util.Objects; @@ -306,6 +309,34 @@ public abstract static class AbstractTFamilyBuilder . - @prefix owl: . - @prefix rdf: . - @prefix rdfs: . - @prefix xsd: . ns:person1 ns:hasName "John Doe" . @@ -114,10 +110,7 @@ void testRdfTypeShortcut() throws SerializationException { String expected = """ @prefix foaf: . @prefix ns: . - @prefix owl: . @prefix rdf: . - @prefix rdfs: . - @prefix xsd: . ns:person1 a foaf:Person . @@ -164,10 +157,7 @@ void testLiteralWithLanguageTag() throws SerializationException { String expected = """ @prefix 11: . @prefix data: . - @prefix owl: . @prefix rdf: . - @prefix rdfs: . - @prefix xsd: . data:book1 11:title "The Odyssey"@en . @@ -213,9 +203,6 @@ void testLiteralWithExplicitXsdStringType() throws SerializationException { String expected = """ @prefix 11: . @prefix data: . - @prefix owl: . - @prefix rdf: . - @prefix rdfs: . @prefix xsd: . data:book2 11:creator "Homer"^^xsd:string . @@ -260,10 +247,6 @@ void testBaseIRI() throws SerializationException { String expected = """ @base . @prefix base: . - @prefix owl: . - @prefix rdf: . - @prefix rdfs: . - @prefix xsd: . base:resource1 base:prop "Test" . @@ -297,13 +280,7 @@ void testEmptyModel() throws SerializationException { verify(emptyModel, times(2)).stream(); - String expected = """ - @prefix owl: . - @prefix rdf: . - @prefix rdfs: . - @prefix xsd: . - - """; + String expected = ""; String actual = writer.toString().replace("\r\n", "\n"); assertEquals(expected, actual); } @@ -413,11 +390,7 @@ void testMultilineLiteralSerialization() throws SerializationException { String expected = """ @prefix book: . - @prefix owl: . @prefix properties: . - @prefix rdf: . - @prefix rdfs: . - @prefix xsd: . book:1 properties:description\s""" + "\"\"\"" + multilineText + "\"\"\"" + " .\n\n"; @@ -455,10 +428,6 @@ void testBasicTrigSerializationWithNamedGraph() throws SerializationException { String expected = """ @prefix data: . @prefix graph: . - @prefix owl: . - @prefix rdf: . - @prefix rdfs: . - @prefix xsd: . graph:g1 { data:person1 data:name "Alice" . diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerTest.java index 2ab5eb370..2309c1da1 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerTest.java @@ -70,10 +70,6 @@ void testBasicTurtleSerialization() throws SerializationException { String expected = """ @prefix ns: . - @prefix owl: . - @prefix rdf: . - @prefix rdfs: . - @prefix xsd: . ns:person1 ns:hasName "John Doe" . """; @@ -104,6 +100,9 @@ void testRdfTypeShortcut() throws SerializationException { .thenReturn(Stream.of(mockStatement)); StringWriter writer = new StringWriter(); + TurtleSerializerOptions options = TurtleSerializerOptions.builder() + .prefixHandler(new PrefixHandler(true)) + .build(); TurtleSerializer turtleSerializer = new TurtleSerializer(mockModel, defaultConfig); @@ -114,10 +113,7 @@ void testRdfTypeShortcut() throws SerializationException { String expected = """ @prefix foaf: . @prefix ns: . - @prefix owl: . @prefix rdf: . - @prefix rdfs: . - @prefix xsd: . ns:person1 a foaf:Person . """; @@ -172,10 +168,7 @@ void testLiteralWithLanguageTag() throws SerializationException { String expected = """ @prefix 11: . @prefix data: . - @prefix owl: . @prefix rdf: . - @prefix rdfs: . - @prefix xsd: . data:book1 11:title "The Odyssey"@en . """; @@ -209,15 +202,12 @@ void testLiteralWithExplicitXsdStringType() throws SerializationException { StringWriter writer = new StringWriter(); - PrefixHandler prefixHandler = new PrefixHandler(true); - prefixHandler.setPrefix("data", "http://example.org/data/"); - prefixHandler.setPrefix("dc", "http://purl.org/dc/elements/1.1/"); - TurtleSerializerOptions config = new TurtleSerializerOptions.Builder() .literalDatatypePolicy(LiteralDatatypePolicyEnum.ALWAYS_TYPED) .usePrefixes(true) .autoDeclarePrefixes(true) - .prefixHandler(prefixHandler) + .addPrefix("data", "http://example.org/data/") + .addPrefix("dc", "http://purl.org/dc/elements/1.1/") .build(); TurtleSerializer turtleSerializer = new TurtleSerializer(mockModel, config); @@ -229,9 +219,6 @@ void testLiteralWithExplicitXsdStringType() throws SerializationException { String expected = """ @prefix data: . @prefix dc: . - @prefix owl: . - @prefix rdf: . - @prefix rdfs: . @prefix xsd: . data:book2 dc:creator "Homer"^^xsd:string . @@ -396,10 +383,6 @@ void testBaseIRI() throws SerializationException { String expected = """ @base . @prefix base: . - @prefix owl: . - @prefix rdf: . - @prefix rdfs: . - @prefix xsd: . base:resource1 base:prop "Test" . """; @@ -432,13 +415,7 @@ void testEmptyModel() throws SerializationException { verify(emptyModel, times(2)).stream(); - String expected = """ - @prefix owl: . - @prefix rdf: . - @prefix rdfs: . - @prefix xsd: . - - """; + String expected = ""; String actual = writer.toString().replace("\r\n", "\n"); assertEquals(expected, actual); } @@ -546,11 +523,7 @@ void testMultilineLiteralSerialization() throws SerializationException { String expected = """ @prefix book: . - @prefix owl: . @prefix properties: . - @prefix rdf: . - @prefix rdfs: . - @prefix xsd: . book:1 properties:description\s""" + "\"\"\"" + multilineText + "\"\"\"" + " .\n"; From 545c3dd910b759140ef4dc7e2a3123103b4b1598 Mon Sep 17 00:00:00 2001 From: Pierre Maillot Date: Thu, 11 Dec 2025 16:01:58 +0100 Subject: [PATCH 06/12] Fix NS ordering --- .../BlankNodeIdGenerationOptions.java | 2 +- .../DatatypePolicyOptions.java | 2 +- .../LineEndingOptions.java | 2 +- .../PrettyPrintOptions.java | 2 +- .../RDFSerializer.java | 2 +- .../SerializerFactory.java | 2 +- .../UsesPrefixOptions.java | 2 +- .../io/serialization/SerializerFactory.java | 8 +- .../base/AbstractGraphSerializer.java | 7 +- .../base/AbstractLineBasedSerializer.java | 2 +- .../jsonld/JSONLDSerializer.java | 2 +- .../nquads/NQuadsSerializer.java | 4 +- .../ntriples/NTriplesSerializer.java | 5 +- .../option/AbstractSerializerOptions.java | 6 +- .../option/AbstractTFamilyOptions.java | 6 +- .../rdfxml/RDFXMLSerializer.java | 189 +++++++++--------- .../rdfxml/RDFXMLSerializerOptions.java | 5 +- .../io/serialization/trig/TriGSerializer.java | 6 +- .../turtle/TurtleSerializer.java | 4 - .../turtle/TurtleSerializerOptions.java | 4 +- .../io/parser/jsonld/JSONLDCircularTest.java | 5 +- .../io/parser/nquads/NQuadsCircularTest.java | 4 +- .../parser/ntriples/NTriplesCircularTest.java | 4 +- .../io/parser/rdfxml/RDFXMLCircularTest.java | 4 +- .../impl/io/parser/trig/TriGCircularTest.java | 4 +- .../io/parser/turtle/TurtleCircularTest.java | 4 +- .../serialization/SerializerFactoryTest.java | 7 +- .../canonical/RDFC10SerializerTest.java | 2 +- .../jsonld/JSONLDSerializerTest.java | 2 +- .../rdfxml/RDFXMLSerializerTest.java | 33 +-- .../turtle/TurtleSerializerTest.java | 3 + 31 files changed, 155 insertions(+), 179 deletions(-) rename src/main/java/fr/inria/corese/core/next/api/io/{serialization => serializer}/BlankNodeIdGenerationOptions.java (82%) rename src/main/java/fr/inria/corese/core/next/api/io/{serialization => serializer}/DatatypePolicyOptions.java (87%) rename src/main/java/fr/inria/corese/core/next/api/io/{serialization => serializer}/LineEndingOptions.java (55%) rename src/main/java/fr/inria/corese/core/next/api/io/{serialization => serializer}/PrettyPrintOptions.java (95%) rename src/main/java/fr/inria/corese/core/next/api/io/{serialization => serializer}/RDFSerializer.java (96%) rename src/main/java/fr/inria/corese/core/next/api/io/{serialization => serializer}/SerializerFactory.java (96%) rename src/main/java/fr/inria/corese/core/next/api/io/{serialization => serializer}/UsesPrefixOptions.java (94%) diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/BlankNodeIdGenerationOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serializer/BlankNodeIdGenerationOptions.java similarity index 82% rename from src/main/java/fr/inria/corese/core/next/api/io/serialization/BlankNodeIdGenerationOptions.java rename to src/main/java/fr/inria/corese/core/next/api/io/serializer/BlankNodeIdGenerationOptions.java index 479401450..91842391b 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serialization/BlankNodeIdGenerationOptions.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serializer/BlankNodeIdGenerationOptions.java @@ -1,4 +1,4 @@ -package fr.inria.corese.core.next.api.io.serialization; +package fr.inria.corese.core.next.api.io.serializer; public interface BlankNodeIdGenerationOptions { diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/DatatypePolicyOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serializer/DatatypePolicyOptions.java similarity index 87% rename from src/main/java/fr/inria/corese/core/next/api/io/serialization/DatatypePolicyOptions.java rename to src/main/java/fr/inria/corese/core/next/api/io/serializer/DatatypePolicyOptions.java index 1abab55e3..0f55c34a2 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serialization/DatatypePolicyOptions.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serializer/DatatypePolicyOptions.java @@ -1,4 +1,4 @@ -package fr.inria.corese.core.next.api.io.serialization; +package fr.inria.corese.core.next.api.io.serializer; import fr.inria.corese.core.next.impl.io.serialization.option.LiteralDatatypePolicyEnum; diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/LineEndingOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serializer/LineEndingOptions.java similarity index 55% rename from src/main/java/fr/inria/corese/core/next/api/io/serialization/LineEndingOptions.java rename to src/main/java/fr/inria/corese/core/next/api/io/serializer/LineEndingOptions.java index 6fedb34f4..64b226796 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serialization/LineEndingOptions.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serializer/LineEndingOptions.java @@ -1,4 +1,4 @@ -package fr.inria.corese.core.next.api.io.serialization; +package fr.inria.corese.core.next.api.io.serializer; public interface LineEndingOptions { diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/PrettyPrintOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serializer/PrettyPrintOptions.java similarity index 95% rename from src/main/java/fr/inria/corese/core/next/api/io/serialization/PrettyPrintOptions.java rename to src/main/java/fr/inria/corese/core/next/api/io/serializer/PrettyPrintOptions.java index f26935083..6ad5685af 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serialization/PrettyPrintOptions.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serializer/PrettyPrintOptions.java @@ -1,4 +1,4 @@ -package fr.inria.corese.core.next.api.io.serialization; +package fr.inria.corese.core.next.api.io.serializer; import fr.inria.corese.core.next.impl.io.serialization.option.PrefixOrderingEnum; diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/RDFSerializer.java b/src/main/java/fr/inria/corese/core/next/api/io/serializer/RDFSerializer.java similarity index 96% rename from src/main/java/fr/inria/corese/core/next/api/io/serialization/RDFSerializer.java rename to src/main/java/fr/inria/corese/core/next/api/io/serializer/RDFSerializer.java index 1e1501e4f..5e4868522 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serialization/RDFSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serializer/RDFSerializer.java @@ -1,4 +1,4 @@ -package fr.inria.corese.core.next.api.io.serialization; +package fr.inria.corese.core.next.api.io.serializer; import java.io.Writer; diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/SerializerFactory.java b/src/main/java/fr/inria/corese/core/next/api/io/serializer/SerializerFactory.java similarity index 96% rename from src/main/java/fr/inria/corese/core/next/api/io/serialization/SerializerFactory.java rename to src/main/java/fr/inria/corese/core/next/api/io/serializer/SerializerFactory.java index 2deb675b4..7560ff072 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serialization/SerializerFactory.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serializer/SerializerFactory.java @@ -1,4 +1,4 @@ -package fr.inria.corese.core.next.api.io.serialization; +package fr.inria.corese.core.next.api.io.serializer; import fr.inria.corese.core.next.api.Model; import fr.inria.corese.core.next.api.base.io.RDFFormat; diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serialization/UsesPrefixOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serializer/UsesPrefixOptions.java similarity index 94% rename from src/main/java/fr/inria/corese/core/next/api/io/serialization/UsesPrefixOptions.java rename to src/main/java/fr/inria/corese/core/next/api/io/serializer/UsesPrefixOptions.java index c7723b31d..fd0bb51f9 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serialization/UsesPrefixOptions.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serializer/UsesPrefixOptions.java @@ -1,4 +1,4 @@ -package fr.inria.corese.core.next.api.io.serialization; +package fr.inria.corese.core.next.api.io.serializer; import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; import fr.inria.corese.core.next.impl.io.serialization.option.PrefixOrderingEnum; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java index b06573298..3ac796b4d 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java @@ -4,9 +4,7 @@ import fr.inria.corese.core.next.api.ValueFactory; import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.IOOptions; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; -import fr.inria.corese.core.next.impl.exception.SerializationException; -import fr.inria.corese.core.next.impl.io.common.JSONLDOptions; +import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.impl.io.serialization.canonical.RDFC10Canonicalizer; import fr.inria.corese.core.next.impl.io.serialization.canonical.RDFC10Serializer; import fr.inria.corese.core.next.impl.io.serialization.canonical.RDFC10SerializerOptions; @@ -31,7 +29,7 @@ import java.util.function.Function; /** - * Default implementation of {@link fr.inria.corese.core.next.api.io.serialization.SerializerFactory}. + * Default implementation of {@link fr.inria.corese.core.next.api.io.serializer.SerializerFactory}. * This factory is responsible for creating instances of {@link RDFSerializer} * based on the requested {@link RDFFormat}. It uses a registry pattern * to map each format to its corresponding serializer constructor, @@ -44,7 +42,7 @@ * to default configurations if an incompatible type is provided. *

*/ -public class SerializerFactory implements fr.inria.corese.core.next.api.io.serialization.SerializerFactory { +public class SerializerFactory implements fr.inria.corese.core.next.api.io.serializer.SerializerFactory { private final Map> registry; private final Map> defaultRegistry; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java index 9a0becb38..66f1e60b3 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java @@ -2,15 +2,14 @@ import fr.inria.corese.core.next.api.*; import fr.inria.corese.core.next.api.io.IOOptions; -import fr.inria.corese.core.next.api.io.serialization.PrettyPrintOptions; -import fr.inria.corese.core.next.api.io.serialization.UsesPrefixOptions; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; +import fr.inria.corese.core.next.api.io.serializer.PrettyPrintOptions; +import fr.inria.corese.core.next.api.io.serializer.UsesPrefixOptions; +import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; import fr.inria.corese.core.next.impl.common.util.IRIUtils; import fr.inria.corese.core.next.impl.common.vocabulary.*; import fr.inria.corese.core.next.impl.exception.SerializationException; import fr.inria.corese.core.next.impl.io.serialization.option.*; -import fr.inria.corese.core.next.impl.io.serialization.turtle.TurtleSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractLineBasedSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractLineBasedSerializer.java index a998a7975..6d9298ed9 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractLineBasedSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractLineBasedSerializer.java @@ -18,7 +18,7 @@ import fr.inria.corese.core.next.api.Resource; import fr.inria.corese.core.next.api.Statement; import fr.inria.corese.core.next.api.Value; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; +import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.impl.common.literal.RDF; import fr.inria.corese.core.next.impl.exception.SerializationException; import fr.inria.corese.core.next.impl.io.serialization.option.LiteralDatatypePolicyEnum; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/jsonld/JSONLDSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/jsonld/JSONLDSerializer.java index ef4b03477..5f12daec1 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/jsonld/JSONLDSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/jsonld/JSONLDSerializer.java @@ -6,7 +6,7 @@ import com.apicatalog.jsonld.document.RdfDocument; import fr.inria.corese.core.next.api.Model; import fr.inria.corese.core.next.api.base.io.RDFFormat; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; +import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.impl.exception.SerializationException; import fr.inria.corese.core.next.impl.io.common.JSONLDOptions; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/nquads/NQuadsSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/nquads/NQuadsSerializer.java index fe99a41b9..9c9134460 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/nquads/NQuadsSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/nquads/NQuadsSerializer.java @@ -6,8 +6,8 @@ import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.api.io.common.BaseIRIOptions; -import fr.inria.corese.core.next.api.io.serialization.BlankNodeIdGenerationOptions; -import fr.inria.corese.core.next.api.io.serialization.LineEndingOptions; +import fr.inria.corese.core.next.api.io.serializer.BlankNodeIdGenerationOptions; +import fr.inria.corese.core.next.api.io.serializer.LineEndingOptions; import fr.inria.corese.core.next.impl.io.serialization.base.AbstractLineBasedSerializer; import fr.inria.corese.core.next.impl.io.serialization.ntriples.NTriplesSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.option.AbstractNFamilyOptions; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/ntriples/NTriplesSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/ntriples/NTriplesSerializer.java index 45a318800..eb6c50840 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/ntriples/NTriplesSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/ntriples/NTriplesSerializer.java @@ -7,9 +7,8 @@ import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.api.io.common.BaseIRIOptions; -import fr.inria.corese.core.next.api.io.serialization.BlankNodeIdGenerationOptions; -import fr.inria.corese.core.next.api.io.serialization.DatatypePolicyOptions; -import fr.inria.corese.core.next.api.io.serialization.LineEndingOptions; +import fr.inria.corese.core.next.api.io.serializer.BlankNodeIdGenerationOptions; +import fr.inria.corese.core.next.api.io.serializer.LineEndingOptions; import fr.inria.corese.core.next.impl.io.serialization.option.AbstractNFamilyOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java index 73def386e..3e137c881 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java @@ -2,9 +2,9 @@ import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.api.io.common.BaseIRIOptions; -import fr.inria.corese.core.next.api.io.serialization.BlankNodeIdGenerationOptions; -import fr.inria.corese.core.next.api.io.serialization.DatatypePolicyOptions; -import fr.inria.corese.core.next.api.io.serialization.LineEndingOptions; +import fr.inria.corese.core.next.api.io.serializer.BlankNodeIdGenerationOptions; +import fr.inria.corese.core.next.api.io.serializer.DatatypePolicyOptions; +import fr.inria.corese.core.next.api.io.serializer.LineEndingOptions; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; import java.util.Objects; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java index 66d47d0f6..c1ed6d9b1 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java @@ -1,12 +1,10 @@ package fr.inria.corese.core.next.impl.io.serialization.option; import fr.inria.corese.core.next.api.io.IOOptions; -import fr.inria.corese.core.next.api.io.serialization.PrettyPrintOptions; -import fr.inria.corese.core.next.api.io.serialization.UsesPrefixOptions; +import fr.inria.corese.core.next.api.io.serializer.PrettyPrintOptions; +import fr.inria.corese.core.next.api.io.serializer.UsesPrefixOptions; import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.Map; import java.util.Objects; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java index f97b2afdb..70c968d3a 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java @@ -4,20 +4,14 @@ import java.io.IOException; import java.io.UncheckedIOException; import java.io.Writer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.IOOptions; -import fr.inria.corese.core.next.api.io.serialization.*; +import fr.inria.corese.core.next.api.io.serializer.*; +import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; +import fr.inria.corese.core.next.impl.common.util.IRIUtils; import fr.inria.corese.core.next.impl.common.vocabulary.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,8 +49,7 @@ public class RDFXMLSerializer implements RDFSerializer { private final Model model; private final IOOptions config; - private final Map iriToPrefixMapping; - private final Map prefixToIriMapping; + private final PrefixHandler prefixHandler; private final Map blankNodeIds; private int blankNodeCounter = 0; private List cachedStatements; @@ -82,22 +75,15 @@ public RDFXMLSerializer(Model model) { public RDFXMLSerializer(Model model, IOOptions config) { this.model = Objects.requireNonNull(model, "Model cannot be null"); this.config = Objects.requireNonNull(config, "Configuration cannot be null"); - this.iriToPrefixMapping = new HashMap<>(); - this.prefixToIriMapping = new HashMap<>(); - this.blankNodeIds = new HashMap<>(); - initializePrefixes(); - } - /** - * Initializes prefix mappings by adding custom prefixes from the configuration. - * The custom prefixes map in XmlConfig is expected to be {prefix: namespaceURI}. - */ - private void initializePrefixes() { - if (this.config instanceof UsesPrefixOptions usesPrefixOptions && usesPrefixOptions.usePrefixes()) { - for (Map.Entry entry : usesPrefixOptions.getPrefixHandler().getPrefixMap().entrySet()) { - addPrefixMapping(entry.getValue(), entry.getKey()); - } + if(config instanceof UsesPrefixOptions usesPrefixOptions + && usesPrefixOptions.usePrefixes()) { + this.prefixHandler = usesPrefixOptions.getPrefixHandler(); + } else { + this.prefixHandler = new PrefixHandler(false); + this.prefixHandler.setPrefix(RDF.getVocabularyPreferredPrefix(), RDF.getVocabularyNamespace()); } + this.blankNodeIds = new HashMap<>(); } /** @@ -146,12 +132,16 @@ private void writeXmlDeclaration(Writer writer) throws IOException { * @throws IOException if an I/O error occurs. */ private void writeRdfRootElement(Writer writer) throws IOException { - if (this.config instanceof UsesPrefixOptions usesPrefixOptions && usesPrefixOptions.usePrefixes() && usesPrefixOptions.autoDeclarePrefixes()) { - collectUsedNamespaces(); + Set actuallyUsedNamespaces = new HashSet<>(); + actuallyUsedNamespaces.add(RDF.getVocabularyNamespace()); + if (this.config instanceof UsesPrefixOptions usesPrefixOptions + && usesPrefixOptions.usePrefixes() + && usesPrefixOptions.autoDeclarePrefixes()) { + actuallyUsedNamespaces.addAll(collectUsedNamespaces()); } writer.write(SerializationConstants.RDF_ROOT_START); - writeNamespaceAttributes(writer); + writeNamespaceAttributes(writer, actuallyUsedNamespaces); writer.write(">"); if(this.config instanceof LineEndingOptions lineEndingOptions) { writer.write(lineEndingOptions.getLineEnding()); @@ -162,7 +152,8 @@ private void writeRdfRootElement(Writer writer) throws IOException { List sortedSubjects = new ArrayList<>(statementsBySubject.keySet()); - if (this.config instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.sortSubjects()) { + if (this.config instanceof PrettyPrintOptions prettyPrintOptions + && prettyPrintOptions.sortSubjects()) { Collections.sort(sortedSubjects, Comparator.comparing(Value::stringValue)); } @@ -187,19 +178,19 @@ private void writeRdfRootElement(Writer writer) throws IOException { * @param writer the {@link Writer} to which attributes will be written. * @throws IOException if an I/O error occurs. */ - private void writeNamespaceAttributes(Writer writer) throws IOException { - if (!iriToPrefixMapping.containsKey(RDF.getVocabularyNamespace())) { - addPrefixMapping(RDF.getVocabularyNamespace(), RDF.getVocabularyPreferredPrefix()); + private void writeNamespaceAttributes(Writer writer, Set actuallyUsedNamespaces) throws IOException { + logger.info("actually used Namespaces: {}", actuallyUsedNamespaces); + ArrayList namespacelist = new ArrayList<>(actuallyUsedNamespaces); + if (this.config instanceof PrettyPrintOptions prettyPrintOptions + && prettyPrintOptions.getPrefixOrdering() == PrefixOrderingEnum.ALPHABETICAL) { + namespacelist.sort( + (ns1, ns2) -> + prefixHandler.getPrefix(ns1).compareTo(prefixHandler.getPrefix(ns2))); } - List prefixes = new ArrayList<>(prefixToIriMapping.keySet()); - if (this.config instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.getPrefixOrdering() == PrefixOrderingEnum.ALPHABETICAL) { - Collections.sort(prefixes); - } - - for (String prefix : prefixes) { - String namespaceURI = prefixToIriMapping.get(prefix); - writer.write(String.format(" %s%s=\"%s\"", SerializationConstants.XMLNS_PREFIX, prefix, escapeXmlAttribute(namespaceURI))); + for(String namespace : namespacelist) { + String prefix = this.prefixHandler.getPrefix(namespace); + writer.write(String.format(" %s%s=\"%s\"", SerializationConstants.XMLNS_PREFIX, prefix, escapeXmlAttribute(namespace))); } } @@ -207,26 +198,55 @@ private void writeNamespaceAttributes(Writer writer) throws IOException { * Collects all namespaces used in the model (subjects, predicates, objects, contexts) * and attempts to assign prefixes if auto-declaration is enabled and they are not already mapped. */ - private void collectUsedNamespaces() { - Set namespaces = this.cachedStatements.stream() - .flatMap(stmt -> Arrays.asList( - stmt.getSubject(), - stmt.getPredicate(), - stmt.getObject() - ).stream()) + private Set collectUsedNamespaces() { + // Collecting namespaces of all IRIs in the data + Set potentialNamespaces = this.cachedStatements.stream() + .flatMap(stmt -> { + List values = new ArrayList<>(Arrays.asList( + stmt.getSubject(), + stmt.getPredicate(), + stmt.getObject() + )); + if (stmt.getContext() != null) { + values.add(stmt.getContext()); + } + if(stmt.getObject().isLiteral() + && ((Literal) stmt.getObject()).getDatatype() != null) { + values.add(((Literal) stmt.getObject()).getDatatype()); + } + return values.stream(); + }) + .filter(Objects::nonNull) .filter(Value::isIRI) - .map(v -> getNamespace(v.stringValue())) + .map(v -> IRIUtils.guessNamespace(v.stringValue())) .collect(Collectors.toSet()); - - - namespaces.forEach(namespace -> { - if (!iriToPrefixMapping.containsKey(namespace)) { + logger.info("{}", potentialNamespaces); + + // potential namespaces can contain different candidates that are based on each other. We keep the shortest + Set copyPotentialNamespaces = Set.copyOf(potentialNamespaces); + potentialNamespaces = potentialNamespaces + .stream() + .filter(potentialNamespace -> copyPotentialNamespaces + .stream() + .noneMatch(otherPotentialNamespace -> (! otherPotentialNamespace.equals(potentialNamespace)) && potentialNamespace.startsWith(otherPotentialNamespace))) + .collect(Collectors.toSet()); + logger.info("{}", potentialNamespaces); + + potentialNamespaces.forEach(namespace -> { + logger.info("{} is in PrefixHandler = {}", namespace, this.prefixHandler.hasNamespace(namespace)); + if (! this.prefixHandler.hasNamespace(namespace) && + // removing known namespaces from the list of potential namespaces + this.prefixHandler.getNamespaces() + .stream() + .noneMatch(knownNamespace -> (knownNamespace.startsWith(namespace)))) { String prefix = getSuggestedPrefix(namespace); if (prefix != null) { addPrefixMapping(namespace, prefix); } } }); + + return potentialNamespaces; } @@ -238,15 +258,17 @@ private void collectUsedNamespaces() { * @return The prefixed name (e.g., "foaf:name") or null if no suitable prefix is found. */ private String getPrefixedNameInternal(String iriString) { + logger.info("getPrefixedNameInternal({})", iriString); String longestMatchingNamespace = null; String correspondingPrefix = null; int longestMatchLength = -1; - for (Map.Entry entry : iriToPrefixMapping.entrySet()) { - String namespace = entry.getKey(); - String prefix = entry.getValue(); - + logger.info("{}", this.prefixHandler.getPrefixMap()); + for (String namespace : this.prefixHandler.getNamespaces()) { + logger.info("{} contains {} ? {}", iriString, namespace, iriString.startsWith(namespace)); if (iriString.startsWith(namespace)) { + String prefix = this.prefixHandler.getPrefix(namespace); + logger.info("{} = {}", namespace, prefix); if (namespace.length() > longestMatchLength) { longestMatchLength = namespace.length(); longestMatchingNamespace = namespace; @@ -257,7 +279,7 @@ private String getPrefixedNameInternal(String iriString) { if (longestMatchingNamespace != null) { String localName = iriString.substring(longestMatchingNamespace.length()); - + logger.info("{} = {} : {}", iriString, longestMatchingNamespace, localName); if (localName.isEmpty()) { return correspondingPrefix + SerializationConstants.COLON; } @@ -274,34 +296,33 @@ private String getPrefixedNameInternal(String iriString) { * @param prefix The associated prefix. */ private void addPrefixMapping(String namespaceURI, String prefix) { - if (iriToPrefixMapping.containsKey(namespaceURI)) { - if (iriToPrefixMapping.get(namespaceURI).equals(prefix)) { + if (this.prefixHandler.hasNamespace(namespaceURI)) { + if (this.prefixHandler.getPrefix(namespaceURI).equals(prefix)) { return; } else { if (logger.isWarnEnabled()) { logger.warn("Namespace URI '{}' is already mapped to prefix '{}'. Cannot map to new prefix '{}'. " + "Existing mapping for this namespace will be retained.", - namespaceURI, iriToPrefixMapping.get(namespaceURI), prefix); + namespaceURI, this.prefixHandler.getPrefix(namespaceURI), prefix); } return; } } String effectivePrefix = prefix; - if (prefixToIriMapping.containsKey(prefix)) { - if (!prefixToIriMapping.get(prefix).equals(namespaceURI)) { + if (this.prefixHandler.hasPrefix(prefix)) { + if (! this.prefixHandler.getNamespace(prefix).equals(namespaceURI)) { if (logger.isWarnEnabled()) { logger.warn("Prefix '{}' is already mapped to namespace '{}'. Cannot map to new namespace '{}'. " + "A new unique prefix will be generated for '{}'.", - prefix, prefixToIriMapping.get(prefix), namespaceURI, namespaceURI); + prefix, this.prefixHandler.getNamespace(prefix), namespaceURI, namespaceURI); } effectivePrefix = generateUniquePrefix(prefix); } } - iriToPrefixMapping.put(namespaceURI, effectivePrefix); - prefixToIriMapping.put(effectivePrefix, namespaceURI); + this.prefixHandler.setPrefix(effectivePrefix, namespaceURI); } /** @@ -314,7 +335,7 @@ private void addPrefixMapping(String namespaceURI, String prefix) { private String generateUniquePrefix(String basePrefix) { String candidate = basePrefix; int i = 0; - while (prefixToIriMapping.containsKey(candidate)) { + while (this.prefixHandler.hasPrefix(candidate)) { candidate = basePrefix + (++i); } return candidate; @@ -469,30 +490,6 @@ private boolean shouldWriteDatatype(Literal literal) { && datatypePolicyOptions.getLiteralDatatypePolicy() == LiteralDatatypePolicyEnum.MINIMAL)); } - - /** - * Extracts the namespace URI part from an IRI string. - * This is a common heuristic for RDF IRIs. - * - * @param iriString The full IRI. - * @return The namespace URI part. - */ - private String getNamespace(String iriString) { - int hashIdx = iriString.lastIndexOf(SerializationConstants.HASH); - int slashIdx = iriString.lastIndexOf(SerializationConstants.SLASH); - - if (hashIdx > -1) { - return iriString.substring(0, hashIdx + 1); - } else if (slashIdx > -1 && slashIdx < iriString.length() - 1) { - int dotIdx = iriString.lastIndexOf(SerializationConstants.POINT); - if (dotIdx > slashIdx) { - return iriString.substring(0, slashIdx + 1); - } - return iriString.substring(0, slashIdx + 1); - } - return iriString; - } - /** * Suggests a prefix for a given namespace URI. * Attempts to derive a meaningful prefix or generates a unique one. @@ -501,14 +498,6 @@ private String getNamespace(String iriString) { * @return A suggested prefix, or null if suggestion is not possible. */ private String getSuggestedPrefix(String namespace) { - - if (namespace.equals(RDF.getVocabularyNamespace())) return RDF.getVocabularyPreferredPrefix(); - if (namespace.equals(RDFS.getVocabularyNamespace())) return RDFS.getVocabularyPreferredPrefix(); - if (namespace.equals(XSD.getVocabularyNamespace())) return XSD.getVocabularyPreferredPrefix(); - if (namespace.equals(OWL.getVocabularyNamespace())) return OWL.getVocabularyPreferredPrefix(); - if (namespace.equals(FOAF.getVocabularyNamespace())) return FOAF.getVocabularyPreferredPrefix(); - - String base = namespace; if (base.endsWith(SerializationConstants.HASH) || base.endsWith(SerializationConstants.SLASH)) { base = base.substring(0, base.length() - 1); @@ -540,7 +529,7 @@ private String getSuggestedPrefix(String namespace) { String candidate = base; int i = 0; - while (prefixToIriMapping.containsKey(candidate) && !prefixToIriMapping.get(candidate).equals(namespace)) { + while (this.prefixHandler.hasPrefix(candidate) && !this.prefixHandler.getPrefix(candidate).equals(namespace)) { candidate = base + (++i); } return candidate; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOptions.java index 9c2f00231..64f9198f8 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerOptions.java @@ -1,6 +1,7 @@ package fr.inria.corese.core.next.impl.io.serialization.rdfxml; -import fr.inria.corese.core.next.api.io.serialization.PrettyPrintOptions; +import fr.inria.corese.core.next.api.io.serializer.PrettyPrintOptions; +import fr.inria.corese.core.next.api.io.serializer.UsesPrefixOptions; import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; import fr.inria.corese.core.next.impl.io.serialization.option.AbstractSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.option.LiteralDatatypePolicyEnum; @@ -18,7 +19,7 @@ *

Use the {@link Builder} class to create instances of {@code RDFXMLSerializerOptions}. * A predefined default configuration is available via {@link #defaultConfig()}.

*/ -public class RDFXMLSerializerOptions extends AbstractSerializerOptions implements PrettyPrintOptions { +public class RDFXMLSerializerOptions extends AbstractSerializerOptions implements PrettyPrintOptions, UsesPrefixOptions { /** * Whether prefix declarations (e.g., `xmlns:prefix="uri"`) should be used for compact IRIs. diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializer.java index e5ee9691d..8d692d5af 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializer.java @@ -16,11 +16,9 @@ import fr.inria.corese.core.next.api.Statement; import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.IOOptions; -import fr.inria.corese.core.next.api.io.common.BaseIRIOptions; -import fr.inria.corese.core.next.api.io.serialization.LineEndingOptions; -import fr.inria.corese.core.next.api.io.serialization.PrettyPrintOptions; +import fr.inria.corese.core.next.api.io.serializer.LineEndingOptions; +import fr.inria.corese.core.next.api.io.serializer.PrettyPrintOptions; import fr.inria.corese.core.next.impl.io.serialization.base.AbstractGraphSerializer; -import fr.inria.corese.core.next.impl.io.serialization.option.AbstractSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.option.AbstractTFamilyOptions; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializer.java index eb98bc223..4de909266 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializer.java @@ -6,10 +6,6 @@ import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.IOOptions; -import fr.inria.corese.core.next.api.io.common.BaseIRIOptions; -import fr.inria.corese.core.next.api.io.serialization.LineEndingOptions; -import fr.inria.corese.core.next.api.io.serialization.UsesPrefixOptions; -import fr.inria.corese.core.next.impl.io.serialization.option.AbstractSerializerOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerOptions.java index d3b200fef..8a001358c 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerOptions.java @@ -1,11 +1,9 @@ package fr.inria.corese.core.next.impl.io.serialization.turtle; import fr.inria.corese.core.next.api.io.IOOptions; -import fr.inria.corese.core.next.api.io.serialization.LineEndingOptions; +import fr.inria.corese.core.next.api.io.serializer.LineEndingOptions; import fr.inria.corese.core.next.impl.io.serialization.option.AbstractTFamilyOptions; import fr.inria.corese.core.next.impl.io.serialization.option.BlankNodeStyleEnum; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Configuration for Turtle serialization format. diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/jsonld/JSONLDCircularTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/jsonld/JSONLDCircularTest.java index f196531b5..e3fa9c040 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/jsonld/JSONLDCircularTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/jsonld/JSONLDCircularTest.java @@ -3,8 +3,7 @@ import fr.inria.corese.core.next.api.*; import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.parser.RDFParser; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; -import fr.inria.corese.core.next.impl.io.common.JSONLDOptions; +import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.impl.io.parser.ParserFactory; import fr.inria.corese.core.next.impl.io.serialization.SerializerFactory; import fr.inria.corese.core.next.impl.temp.CoreseAdaptedValueFactory; @@ -33,7 +32,7 @@ class JSONLDCircularTest { private ValueFactory valueFactory; - private fr.inria.corese.core.next.api.io.serialization.SerializerFactory serializerFactory; + private fr.inria.corese.core.next.api.io.serializer.SerializerFactory serializerFactory; private ParserFactory parserFactory; private JSONLDOptions defaultConfig; diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsCircularTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsCircularTest.java index 2ea8b638d..1bad1a5cc 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsCircularTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsCircularTest.java @@ -3,7 +3,7 @@ import fr.inria.corese.core.next.api.*; import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.parser.RDFParser; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; +import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.impl.io.parser.ParserFactory; import fr.inria.corese.core.next.impl.io.serialization.SerializerFactory; import fr.inria.corese.core.next.impl.io.serialization.nquads.NQuadsSerializerOptions; @@ -32,7 +32,7 @@ class NQuadsCircularTest { private ValueFactory valueFactory; - private fr.inria.corese.core.next.api.io.serialization.SerializerFactory serializerFactory; + private fr.inria.corese.core.next.api.io.serializer.SerializerFactory serializerFactory; private ParserFactory parserFactory; private NQuadsSerializerOptions defaultConfig; diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesCircularTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesCircularTest.java index bbfc741be..155b601a7 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesCircularTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesCircularTest.java @@ -3,7 +3,7 @@ import fr.inria.corese.core.next.api.*; import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.parser.RDFParser; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; +import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.impl.io.parser.ParserFactory; import fr.inria.corese.core.next.impl.io.serialization.SerializerFactory; import fr.inria.corese.core.next.impl.io.serialization.ntriples.NTriplesSerializerOptions; @@ -30,7 +30,7 @@ class NTriplesCircularTest { private ValueFactory valueFactory; - private fr.inria.corese.core.next.api.io.serialization.SerializerFactory serializerFactory; + private fr.inria.corese.core.next.api.io.serializer.SerializerFactory serializerFactory; private ParserFactory parserFactory; private NTriplesSerializerOptions defaultConfig; diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/rdfxml/RDFXMLCircularTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/rdfxml/RDFXMLCircularTest.java index 28af64e60..491ca3502 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/rdfxml/RDFXMLCircularTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/rdfxml/RDFXMLCircularTest.java @@ -3,7 +3,7 @@ import fr.inria.corese.core.next.api.*; import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.parser.RDFParser; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; +import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.impl.io.parser.ParserFactory; import fr.inria.corese.core.next.impl.io.serialization.SerializerFactory; import fr.inria.corese.core.next.impl.io.serialization.rdfxml.RDFXMLSerializerOptions; @@ -36,7 +36,7 @@ class RDFXMLCircularTest { private ValueFactory valueFactory; - private fr.inria.corese.core.next.api.io.serialization.SerializerFactory serializerFactory; + private fr.inria.corese.core.next.api.io.serializer.SerializerFactory serializerFactory; private ParserFactory parserFactory; private RDFXMLSerializerOptions defaultConfig; diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/trig/TriGCircularTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/trig/TriGCircularTest.java index 0fa9d4720..9cf5fdaa6 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/trig/TriGCircularTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/trig/TriGCircularTest.java @@ -15,7 +15,7 @@ import fr.inria.corese.core.next.api.ValueFactory; import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.parser.RDFParser; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; +import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.impl.io.parser.ParserFactory; import fr.inria.corese.core.next.impl.io.serialization.SerializerFactory; import fr.inria.corese.core.next.impl.io.serialization.trig.TriGSerializerOptions; @@ -37,7 +37,7 @@ class TriGCircularTest { private ValueFactory valueFactory; - private fr.inria.corese.core.next.api.io.serialization.SerializerFactory serializerFactory; + private fr.inria.corese.core.next.api.io.serializer.SerializerFactory serializerFactory; private ParserFactory parserFactory; private TriGSerializerOptions defaultConfig; diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/turtle/TurtleCircularTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/turtle/TurtleCircularTest.java index 848c1336e..ab59f1c80 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/turtle/TurtleCircularTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/turtle/TurtleCircularTest.java @@ -15,7 +15,7 @@ import fr.inria.corese.core.next.api.ValueFactory; import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.parser.RDFParser; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; +import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.impl.io.parser.ParserFactory; import fr.inria.corese.core.next.impl.io.serialization.SerializerFactory; import fr.inria.corese.core.next.impl.io.serialization.turtle.TurtleSerializerOptions; @@ -35,7 +35,7 @@ class TurtleCircularTest { private ValueFactory valueFactory; - private fr.inria.corese.core.next.api.io.serialization.SerializerFactory serializerFactory; + private fr.inria.corese.core.next.api.io.serializer.SerializerFactory serializerFactory; private ParserFactory parserFactory; private TurtleSerializerOptions defaultConfig; diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactoryTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactoryTest.java index d2d041c09..807e199a9 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactoryTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactoryTest.java @@ -2,12 +2,7 @@ import fr.inria.corese.core.next.api.Model; import fr.inria.corese.core.next.api.base.io.RDFFormat; -import fr.inria.corese.core.next.api.io.IOOptions; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; -import fr.inria.corese.core.next.impl.exception.SerializationException; -import fr.inria.corese.core.next.impl.io.common.JSONLDOptions; -import fr.inria.corese.core.next.impl.io.serialization.canonical.RDFC10Serializer; -import fr.inria.corese.core.next.impl.io.serialization.canonical.RDFC10SerializerOptions; +import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.impl.io.serialization.jsonld.JSONLDSerializer; import fr.inria.corese.core.next.impl.io.serialization.nquads.NQuadsSerializer; import fr.inria.corese.core.next.impl.io.serialization.nquads.NQuadsSerializerOptions; diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/canonical/RDFC10SerializerTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/canonical/RDFC10SerializerTest.java index 7233acf46..ce017b542 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/canonical/RDFC10SerializerTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/canonical/RDFC10SerializerTest.java @@ -3,7 +3,7 @@ import fr.inria.corese.core.next.api.*; import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.parser.RDFParser; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; +import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.impl.exception.SerializationException; import fr.inria.corese.core.next.impl.io.parser.ParserFactory; import fr.inria.corese.core.next.impl.io.serialization.SerializerFactory; diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/jsonld/JSONLDSerializerTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/jsonld/JSONLDSerializerTest.java index 0a758c136..a22938244 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/jsonld/JSONLDSerializerTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/jsonld/JSONLDSerializerTest.java @@ -2,7 +2,7 @@ import com.apicatalog.jsonld.json.JsonLdComparison; import fr.inria.corese.core.next.api.*; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; +import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.impl.io.common.JSONLDOptions; import fr.inria.corese.core.next.impl.temp.CoreseAdaptedValueFactory; import fr.inria.corese.core.next.impl.temp.CoreseModel; diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java index 6713b69e1..7eb22e8fe 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java @@ -2,6 +2,7 @@ import fr.inria.corese.core.next.api.Model; import fr.inria.corese.core.next.api.Statement; +import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; import fr.inria.corese.core.next.impl.common.vocabulary.XSD; import fr.inria.corese.core.next.impl.exception.SerializationException; import fr.inria.corese.core.next.impl.io.serialization.TestStatementFactory; @@ -13,6 +14,8 @@ import org.junit.jupiter.api.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.StringWriter; import java.util.stream.Stream; @@ -26,6 +29,8 @@ */ class RDFXMLSerializerTest { + private static final Logger logger = LoggerFactory.getLogger(RDFXMLSerializerTest.class); + @Mock private Model mockModel; RDFXMLSerializerOptions mockConfig; @@ -65,14 +70,13 @@ void shouldSerializeSimpleIriTriple() throws SerializationException { .prefixOrdering(PrefixOrderingEnum.ALPHABETICAL) .build(); - RDFXMLSerializer serializer = new RDFXMLSerializer(mockModel, testConfig); serializer.write(writer); String expected = """ - + @@ -104,7 +108,7 @@ void shouldHandleBlankNodeSubject() throws SerializationException { String expected = """ - + @@ -138,7 +142,7 @@ void shouldHandleBlankNodeObject() throws SerializationException { String expected = """ - + @@ -170,7 +174,7 @@ void shouldSerializeLiteralWithStringDatatypeMinimalPolicy() throws Serializatio String expected = """ - + John Doe @@ -202,7 +206,7 @@ void shouldSerializeLiteralWithCustomDatatypeMinimalPolicy() throws Serializatio String expected = """ - + 123 @@ -232,7 +236,7 @@ void shouldSerializeLiteralWithLanguage() throws SerializationException { String expected = """ - + The Book @@ -276,7 +280,6 @@ void shouldRespectPrefixOrderingDefault() throws SerializationException { assertTrue(actual.contains("xmlns:exorg=\"http://ex.org/\"")); assertTrue(actual.contains("xmlns:excom=\"http://ex.com/\"")); - assertTrue(actual.contains("xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"")); String desc1 = " \n \n "; String desc2 = " \n \n "; @@ -312,7 +315,7 @@ void shouldSortSubjectsAlphabetically() throws SerializationException { String expected = """ - + @@ -346,7 +349,7 @@ void shouldEscapeXmlAttributeValues() throws SerializationException { String expected = """ - + @@ -379,7 +382,7 @@ void shouldEscapeXmlContentValues() throws SerializationException { String expected = """ - + Value with <tags> & entities @@ -412,9 +415,9 @@ void shouldNotAutoDeclarePrefixesIfDisabled() throws SerializationException { String expected = """ - + - + """; @@ -484,7 +487,7 @@ void shouldNotGenerateStableBlankNodeIds() throws SerializationException { String expected = """ - + @@ -511,7 +514,7 @@ void shouldHandleEmptyModel() throws SerializationException { String expected = """ - + """; diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerTest.java index 2309c1da1..1a7976689 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerTest.java @@ -7,6 +7,7 @@ import fr.inria.corese.core.next.impl.exception.SerializationException; import fr.inria.corese.core.next.impl.io.serialization.TestStatementFactory; import fr.inria.corese.core.next.impl.io.serialization.option.LiteralDatatypePolicyEnum; +import fr.inria.corese.core.next.impl.exception.SerializationException; import fr.inria.corese.core.next.impl.temp.CoreseAdaptedValueFactory; import fr.inria.corese.core.next.impl.temp.CoreseModel; import org.junit.jupiter.api.BeforeEach; @@ -294,6 +295,8 @@ void testBlankNodeSerializarionWithoutId() { Logger logger = LoggerFactory.getLogger(TurtleSerializerTest.class); ValueFactory valueFactory; + fr.inria.corese.core.next.api.io.serializer.SerializerFactory serializerFactory; + ParserFactory parserFactory; TurtleSerializerOptions defaultConfig; String EXAMPLE_NS = "http://example.org/"; String PREDICATE_KNOWS = EXAMPLE_NS + "knows"; From bdd4a4e150d6502735ccba82016bde5d2c02827c Mon Sep 17 00:00:00 2001 From: Pierre Maillot Date: Mon, 15 Dec 2025 16:43:28 +0100 Subject: [PATCH 07/12] escaped URIs in XML --- .../corese/core/next/api/IPrefixHandler.java | 7 ++ .../impl/common/prefix/PrefixHandler.java | 17 +++- .../core/next/impl/common/util/IRIUtils.java | 43 +++++---- .../io/serialization/SerializerFactory.java | 6 +- .../rdfxml/RDFXMLSerializer.java | 90 ++++++------------- .../next/impl/common/util/IRIUtilsTest.java | 6 ++ .../io/parser/jsonld/JSONLDCircularTest.java | 1 + .../serialization/SerializerFactoryTest.java | 9 +- .../rdfxml/RDFXMLSerializerTest.java | 6 +- .../turtle/TurtleSerializerTest.java | 3 +- 10 files changed, 100 insertions(+), 88 deletions(-) diff --git a/src/main/java/fr/inria/corese/core/next/api/IPrefixHandler.java b/src/main/java/fr/inria/corese/core/next/api/IPrefixHandler.java index 29742fee9..e2555c2d5 100644 --- a/src/main/java/fr/inria/corese/core/next/api/IPrefixHandler.java +++ b/src/main/java/fr/inria/corese/core/next/api/IPrefixHandler.java @@ -68,6 +68,13 @@ public interface IPrefixHandler { */ Map getPrefixMap(); + /** + * Returns all namespaces mappings as an unmodifiable map. + * + * @return an unmodifiable map where keys are namespaces IRIs and values are prefixes + */ + Map getNamespaceMap(); + /** * Returns all namespace objects as an immutable set. * Each Namespace object contains both prefix and IRI. diff --git a/src/main/java/fr/inria/corese/core/next/impl/common/prefix/PrefixHandler.java b/src/main/java/fr/inria/corese/core/next/impl/common/prefix/PrefixHandler.java index 0403836a8..402457f60 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/common/prefix/PrefixHandler.java +++ b/src/main/java/fr/inria/corese/core/next/impl/common/prefix/PrefixHandler.java @@ -90,10 +90,15 @@ public void setPrefix(String prefix, String namespace) { } String oldNamespace = prefixToNamespace.get(prefix); - if (oldNamespace != null && !oldNamespace.equals(namespace)) { + if (oldNamespace != null && ! oldNamespace.equals(namespace)) { namespaceToPrefix.remove(oldNamespace); prefixToNamespace.remove(prefix); } + String oldPrefix = namespaceToPrefix.get(namespace); + if(oldPrefix != null && ! oldPrefix.equals(prefix)) { + namespaceToPrefix.remove(namespace); + prefixToNamespace.remove(oldPrefix); + } prefixToNamespace.put(prefix, namespace); namespaceToPrefix.put(namespace, prefix); @@ -227,6 +232,16 @@ public Map getPrefixMap() { return Collections.unmodifiableMap(new HashMap<>(prefixToNamespace)); } + /** + * Returns all namespaces mappings as an unmodifiable map. + * + * @return an unmodifiable map where keys are namespaces IRIs and values are prefixes + */ + @Override + public Map getNamespaceMap() { + return Collections.unmodifiableMap(new HashMap<>(namespaceToPrefix)); + } + /** * Returns all namespace objects as an immutable set. * Each Namespace object contains both prefix and IRI. diff --git a/src/main/java/fr/inria/corese/core/next/impl/common/util/IRIUtils.java b/src/main/java/fr/inria/corese/core/next/impl/common/util/IRIUtils.java index 55a04a6c5..d5889fd09 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/common/util/IRIUtils.java +++ b/src/main/java/fr/inria/corese/core/next/impl/common/util/IRIUtils.java @@ -1,5 +1,8 @@ package fr.inria.corese.core.next.impl.common.util; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.net.URI; import java.net.URISyntaxException; import java.util.Set; @@ -14,14 +17,21 @@ */ public class IRIUtils { - private static final Pattern IRI_PATTERN = Pattern.compile("^(?" + - "(?[\\w\\-]+):(?\\/\\/)?" + - "(?([\\w\\-_:@]+\\.)*[\\w\\-_:]*))" + - "((?\\/([\\w\\-\\._\\:]+\\/)*)" + - "(?[\\w\\-\\._\\:]+)?" + - "(?\\?[\\w\\-_\\:\\?\\=]+)?" + - "(?(\\#))?" + - "(?([\\w\\-_]+))?)?$"); + private static final Logger logger = LoggerFactory.getLogger(IRIUtils.class); + + // Example 1 : http://webisa.webdatacommons.org/data/sparql?query=q#line1 + private static final Pattern IRI_PATTERN = Pattern.compile("^(?" + // http://webisa.webdatacommons.org + "(?[\\w\\-]+):" + // http: + "(?\\/\\/)?" + // // + "(?([\\w\\[\\]\\-_:@]+\\.)*[\\w\\-_:]*)" + // webisa.webdatacommons.org + ")" + + "(?\\/([\\w\\-\\._\\:]+\\/)*)*" + // /data/ + "(?[\\w&<>;\\-\\._\\:\\\"\\\']+)?" + // sparql + "(?\\?[\\w\\-\\\"\\\'_\\:\\?\\=]+)?" + // ?query=q + "(?\\#)?" + // # + "(?[\\w\\-_]+)?" + // line1 + "$" + ); private static final Pattern STANDARD_IRI_PATTERN = Pattern.compile("^(([^:/?#\\s]+):)(\\/\\/([^/?#\\s]*))?([^?#\\s]*)(\\?([^#\\s]*))?(#(.*))?"); private static final int MAX_IRI_LENGTH = 2048; private static final long REGEX_TIMEOUT_MS = 100; @@ -40,11 +50,13 @@ private IRIUtils() { * @return the guessed namespace of the IRI or an empty string if no match is found. */ public static String guessNamespace(String iri) { + logger.info("guessNamespace {}", iri); if (isInvalidInput(iri)) { return ""; } try { Matcher matcher = matchWithTimeout(IRI_PATTERN, iri); + logger.info("{} : {}", iri, matcher.matches()); if (matcher == null || !matcher.matches()) { if (iri.endsWith("#")) { return iri; @@ -54,20 +66,21 @@ public static String guessNamespace(String iri) { return iri; } } else if (matcher.matches()) { + // This is a blank node if (matcher.group("protocol") != null && matcher.group("protocol").equals("_")) { return ""; } + StringBuilder namespace = new StringBuilder(); - namespace.append(matcher.group("protocol")).append(":"); - if (matcher.group("dblSlashes") != null) { - namespace.append(matcher.group("dblSlashes")); - } - namespace.append(matcher.group("domain")); + namespace.append(matcher.group("rootnamespace")); if (matcher.group("path") != null) { namespace.append(matcher.group("path")); } - if((matcher.group("fragment") != null || matcher.group("anchor") != null) && matcher.group("finalPath") != null) { - namespace.append(matcher.group("finalPath")).append("#"); + if(matcher.group("anchor") != null) { + if(matcher.group("finalPath") != null) { + namespace.append(matcher.group("finalPath")); + } + namespace.append(matcher.group("anchor")); } return namespace.toString(); diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java index 3ac796b4d..a0183829d 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java @@ -5,6 +5,8 @@ import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; +import fr.inria.corese.core.next.impl.exception.SerializationException; +import fr.inria.corese.core.next.impl.io.common.JSONLDOptions; import fr.inria.corese.core.next.impl.io.serialization.canonical.RDFC10Canonicalizer; import fr.inria.corese.core.next.impl.io.serialization.canonical.RDFC10Serializer; import fr.inria.corese.core.next.impl.io.serialization.canonical.RDFC10SerializerOptions; @@ -14,7 +16,7 @@ import fr.inria.corese.core.next.impl.io.serialization.ntriples.NTriplesSerializer; import fr.inria.corese.core.next.impl.io.serialization.ntriples.NTriplesSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.rdfxml.RDFXMLSerializer; -import fr.inria.corese.core.next.impl.io.serialization.rdfxml.RDFXMLSerializerOption; +import fr.inria.corese.core.next.impl.io.serialization.rdfxml.RDFXMLSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.trig.TriGSerializer; import fr.inria.corese.core.next.impl.io.serialization.trig.TriGSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.turtle.TurtleSerializer; @@ -112,7 +114,7 @@ public SerializerFactory() { tempDefaultRegistry.put(RDFFormat.TRIG, TriGSerializer::new); tempRegistry.put(RDFFormat.RDFXML, (model, genericConfig) -> { - if (genericConfig instanceof RDFXMLSerializerOption specificConfig) { + if (genericConfig instanceof RDFXMLSerializerOptions specificConfig) { return new RDFXMLSerializer(model, specificConfig); } throw new SerializationException( diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java index 70c968d3a..6ecd81b5b 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java @@ -81,7 +81,9 @@ public RDFXMLSerializer(Model model, IOOptions config) { this.prefixHandler = usesPrefixOptions.getPrefixHandler(); } else { this.prefixHandler = new PrefixHandler(false); + // These namespaces are part of the RDF/XML standard this.prefixHandler.setPrefix(RDF.getVocabularyPreferredPrefix(), RDF.getVocabularyNamespace()); + this.prefixHandler.setPrefix(XSD.getVocabularyPreferredPrefix(), XSD.getVocabularyNamespace()); } this.blankNodeIds = new HashMap<>(); } @@ -180,12 +182,33 @@ private void writeRdfRootElement(Writer writer) throws IOException { */ private void writeNamespaceAttributes(Writer writer, Set actuallyUsedNamespaces) throws IOException { logger.info("actually used Namespaces: {}", actuallyUsedNamespaces); + logger.info("Known prefixes: {}", this.prefixHandler.getPrefixMap()); + logger.info("Known namespaces: {}", this.prefixHandler.getNamespaceMap()); ArrayList namespacelist = new ArrayList<>(actuallyUsedNamespaces); + + if(this.config instanceof UsesPrefixOptions usesPrefixOptions + && usesPrefixOptions.autoDeclarePrefixes()) { + logger.info("{}", namespacelist); + + namespacelist.forEach(namespace -> { + logger.info("{} is in PrefixHandler = {}", namespace, this.prefixHandler.hasNamespace(namespace)); + if (! this.prefixHandler.hasNamespace(namespace)) { + String prefix = getSuggestedPrefix(namespace); + if (prefix != null) { + this.prefixHandler.setPrefix(prefix, namespace); + } + } + }); + } + if (this.config instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.getPrefixOrdering() == PrefixOrderingEnum.ALPHABETICAL) { namespacelist.sort( - (ns1, ns2) -> - prefixHandler.getPrefix(ns1).compareTo(prefixHandler.getPrefix(ns2))); + (ns1, ns2) -> { + logger.info("{}: {} <> {}: {}", ns1, prefixHandler.getPrefix(ns1), ns2, prefixHandler.getPrefix(ns2)); + return prefixHandler.getPrefix(ns1).compareTo(prefixHandler.getPrefix(ns2)); + } + ); } for(String namespace : namespacelist) { @@ -220,31 +243,7 @@ private Set collectUsedNamespaces() { .filter(Value::isIRI) .map(v -> IRIUtils.guessNamespace(v.stringValue())) .collect(Collectors.toSet()); - logger.info("{}", potentialNamespaces); - - // potential namespaces can contain different candidates that are based on each other. We keep the shortest - Set copyPotentialNamespaces = Set.copyOf(potentialNamespaces); - potentialNamespaces = potentialNamespaces - .stream() - .filter(potentialNamespace -> copyPotentialNamespaces - .stream() - .noneMatch(otherPotentialNamespace -> (! otherPotentialNamespace.equals(potentialNamespace)) && potentialNamespace.startsWith(otherPotentialNamespace))) - .collect(Collectors.toSet()); - logger.info("{}", potentialNamespaces); - - potentialNamespaces.forEach(namespace -> { - logger.info("{} is in PrefixHandler = {}", namespace, this.prefixHandler.hasNamespace(namespace)); - if (! this.prefixHandler.hasNamespace(namespace) && - // removing known namespaces from the list of potential namespaces - this.prefixHandler.getNamespaces() - .stream() - .noneMatch(knownNamespace -> (knownNamespace.startsWith(namespace)))) { - String prefix = getSuggestedPrefix(namespace); - if (prefix != null) { - addPrefixMapping(namespace, prefix); - } - } - }); + logger.info("Potential namespaces{}", potentialNamespaces); return potentialNamespaces; } @@ -288,43 +287,6 @@ private String getPrefixedNameInternal(String iriString) { return null; } - /** - * Adds a prefix-namespace URI mapping to the internal mappings. - * Handles potential conflicts to ensure uniqueness. - * - * @param namespaceURI The namespace URI. - * @param prefix The associated prefix. - */ - private void addPrefixMapping(String namespaceURI, String prefix) { - if (this.prefixHandler.hasNamespace(namespaceURI)) { - if (this.prefixHandler.getPrefix(namespaceURI).equals(prefix)) { - return; - } else { - - if (logger.isWarnEnabled()) { - logger.warn("Namespace URI '{}' is already mapped to prefix '{}'. Cannot map to new prefix '{}'. " + - "Existing mapping for this namespace will be retained.", - namespaceURI, this.prefixHandler.getPrefix(namespaceURI), prefix); - } - return; - } - } - - String effectivePrefix = prefix; - if (this.prefixHandler.hasPrefix(prefix)) { - if (! this.prefixHandler.getNamespace(prefix).equals(namespaceURI)) { - if (logger.isWarnEnabled()) { - logger.warn("Prefix '{}' is already mapped to namespace '{}'. Cannot map to new namespace '{}'. " + - "A new unique prefix will be generated for '{}'.", - prefix, this.prefixHandler.getNamespace(prefix), namespaceURI, namespaceURI); - } - effectivePrefix = generateUniquePrefix(prefix); - } - } - - this.prefixHandler.setPrefix(effectivePrefix, namespaceURI); - } - /** * Generates a unique prefix based on a given base string, ensuring it's not already in use. * This method appends numbers to the base prefix until a unique one is found. diff --git a/src/test/java/fr/inria/corese/core/next/impl/common/util/IRIUtilsTest.java b/src/test/java/fr/inria/corese/core/next/impl/common/util/IRIUtilsTest.java index 4336896b5..b1626962b 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/common/util/IRIUtilsTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/common/util/IRIUtilsTest.java @@ -20,6 +20,8 @@ public class IRIUtilsTest { private static final String uriToHTMLPageWithQueryAndFragment = "https://www.syuno-pit.biz/tezukayama-bandai-2.html?query=1#fragment"; private static final String uriToHTMLPageWithFragment = "https://www.syuno-pit.biz/tezukayama-bandai-2.html#fragment"; private static final String blankNode = "_:n2d65906b09534cabb44314ff2e2b248axb4"; + private static final String uriWithUnexpectedCharactersObject = "http://example.org/obj"ect&apos"; + private static final String uriWithUnexpectedCharactersSubject = "http://example.org/sub&ject"; // Array of strings that should be recognized as correct IRIs. Some of them taken from the official IRI documentation. private static final String[] correctARIs = { uriSchema, uriWithFragment, uriWithQuery, uriWithPort, uriWithPortAndQuery, uriWithPortAndQueryAndFragment, uriWithPortAndFragment, uriToHTMLPage, uriToHTMLPageWithQuery, uriToHTMLPageWithQueryAndFragment, uriToHTMLPageWithFragment, "ftp://ftp.is.co.za/rfc/rfc1808.txt", "http://www.ietf.org/rfc/rfc2396.txt", "ldap://[2001:db8::7]/c=GB?objectClass?one", "mailto:John.Doe@example.com", "news:comp.infosystems.www.servers.unix", "tel:+1-816-555-1212", "telnet://192.0.2.16:80/", "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", "http://foo.co.uk/", "http://regexr.com/foo.html?q=bar" }; @@ -40,6 +42,8 @@ public void guessNamespaceTest() { assertEquals("https://www.syuno-pit.biz/tezukayama-bandai-2.html#", IRIUtils.guessNamespace(uriToHTMLPageWithFragment)); assertEquals("", IRIUtils.guessNamespace(blankNode)); assertEquals("http://www.w3.org/2001/XMLSchema#", IRIUtils.guessNamespace("http://www.w3.org/2001/XMLSchema#")); + assertEquals("http://example.org/", IRIUtils.guessNamespace(uriWithUnexpectedCharactersObject)); + assertEquals("http://example.org/", IRIUtils.guessNamespace(uriWithUnexpectedCharactersSubject)); } @Test @@ -56,6 +60,8 @@ public void guessLocalNameTest() { assertEquals("fragment", IRIUtils.guessLocalName(uriToHTMLPageWithQueryAndFragment)); assertEquals("fragment", IRIUtils.guessLocalName(uriToHTMLPageWithFragment)); assertEquals("", IRIUtils.guessLocalName(blankNode)); + assertEquals("obj"ect&apos", IRIUtils.guessLocalName(uriWithUnexpectedCharactersObject)); + assertEquals("sub&ject", IRIUtils.guessLocalName(uriWithUnexpectedCharactersSubject)); } @Test diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/jsonld/JSONLDCircularTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/jsonld/JSONLDCircularTest.java index e3fa9c040..c79c85d3c 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/jsonld/JSONLDCircularTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/jsonld/JSONLDCircularTest.java @@ -4,6 +4,7 @@ import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.parser.RDFParser; import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; +import fr.inria.corese.core.next.impl.io.common.JSONLDOptions; import fr.inria.corese.core.next.impl.io.parser.ParserFactory; import fr.inria.corese.core.next.impl.io.serialization.SerializerFactory; import fr.inria.corese.core.next.impl.temp.CoreseAdaptedValueFactory; diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactoryTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactoryTest.java index 807e199a9..196d32d63 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactoryTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactoryTest.java @@ -2,14 +2,19 @@ import fr.inria.corese.core.next.api.Model; import fr.inria.corese.core.next.api.base.io.RDFFormat; +import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; +import fr.inria.corese.core.next.impl.exception.SerializationException; +import fr.inria.corese.core.next.impl.io.common.JSONLDOptions; +import fr.inria.corese.core.next.impl.io.serialization.canonical.RDFC10Serializer; +import fr.inria.corese.core.next.impl.io.serialization.canonical.RDFC10SerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.jsonld.JSONLDSerializer; import fr.inria.corese.core.next.impl.io.serialization.nquads.NQuadsSerializer; import fr.inria.corese.core.next.impl.io.serialization.nquads.NQuadsSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.ntriples.NTriplesSerializer; import fr.inria.corese.core.next.impl.io.serialization.ntriples.NTriplesSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.rdfxml.RDFXMLSerializer; -import fr.inria.corese.core.next.impl.io.serialization.rdfxml.RDFXMLSerializerOption; +import fr.inria.corese.core.next.impl.io.serialization.rdfxml.RDFXMLSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.trig.TriGSerializer; import fr.inria.corese.core.next.impl.io.serialization.trig.TriGSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.turtle.TurtleSerializer; @@ -99,7 +104,7 @@ void createSerializer_shouldReturnTriGSerializer_forTriGFormat() { @Test @DisplayName("createSerializer should return XmlSerializer for RDFXML format") void createSerializer_shouldReturnXmlSerializer_forRdfXmlFormat() { - RDFXMLSerializerOption config = RDFXMLSerializerOption.defaultConfig(); + RDFXMLSerializerOptions config = RDFXMLSerializerOptions.defaultConfig(); try (MockedConstruction mockedConstruction = mockConstruction(RDFXMLSerializer.class)) { RDFSerializer serializer = factory.createSerializer(RDFFormat.RDFXML, mockModel, config); diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java index 7eb22e8fe..4a3cbe020 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java @@ -43,7 +43,7 @@ void setUp() { closeable = MockitoAnnotations.openMocks(this); writer = new StringWriter(); factory = new TestStatementFactory(); - mockConfig = RDFXMLSerializerOption.defaultConfig(); + mockConfig = RDFXMLSerializerOptions.defaultConfig(); } @AfterEach @@ -201,12 +201,14 @@ void shouldSerializeLiteralWithCustomDatatypeMinimalPolicy() throws Serializatio .prefixOrdering(PrefixOrderingEnum.ALPHABETICAL) .build(); + logger.info("AutoDeclarePrefix ? {}", testConfig.autoDeclarePrefixes()); + RDFXMLSerializer serializer = new RDFXMLSerializer(mockModel, testConfig); serializer.write(writer); String expected = """ - + 123 diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerTest.java index 1a7976689..4a33b006e 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializerTest.java @@ -5,6 +5,7 @@ import fr.inria.corese.core.next.impl.common.literal.XSD; import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; import fr.inria.corese.core.next.impl.exception.SerializationException; +import fr.inria.corese.core.next.impl.io.parser.ParserFactory; import fr.inria.corese.core.next.impl.io.serialization.TestStatementFactory; import fr.inria.corese.core.next.impl.io.serialization.option.LiteralDatatypePolicyEnum; import fr.inria.corese.core.next.impl.exception.SerializationException; @@ -295,8 +296,6 @@ void testBlankNodeSerializarionWithoutId() { Logger logger = LoggerFactory.getLogger(TurtleSerializerTest.class); ValueFactory valueFactory; - fr.inria.corese.core.next.api.io.serializer.SerializerFactory serializerFactory; - ParserFactory parserFactory; TurtleSerializerOptions defaultConfig; String EXAMPLE_NS = "http://example.org/"; String PREDICATE_KNOWS = EXAMPLE_NS + "knows"; From eae9f86251fd6abd650bf568d269b1fa6eb2590f Mon Sep 17 00:00:00 2001 From: Pierre Maillot Date: Mon, 15 Dec 2025 16:50:47 +0100 Subject: [PATCH 08/12] Fixing tests that removed # --- .../impl/io/parser/rdfxml/RDFXMLUtilsTest.java | 2 +- .../canonical/RDFC10SerializerTest.java | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/rdfxml/RDFXMLUtilsTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/rdfxml/RDFXMLUtilsTest.java index 727d6209f..592ff3623 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/rdfxml/RDFXMLUtilsTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/rdfxml/RDFXMLUtilsTest.java @@ -75,7 +75,7 @@ public void testExtractSubjectWithID() { AttributesImpl attrs = new AttributesImpl(); attrs.addAttribute(RDF.type.getNamespace(), "ID", "", "CDATA", "id123"); Resource subject = RDFXMLUtils.extractSubject(attrs, factory, "http://example.org/", null); - assertEquals("http://example.org/id123", subject.stringValue()); + assertEquals("http://example.org/#id123", subject.stringValue()); } /** diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/canonical/RDFC10SerializerTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/canonical/RDFC10SerializerTest.java index ce017b542..a258dc44e 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/canonical/RDFC10SerializerTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/canonical/RDFC10SerializerTest.java @@ -297,11 +297,11 @@ void testSerializeFigure3() { assertFalse(canonicalOutput.isEmpty(), "Canonical output should not be empty"); String actual = canonicalOutput.trim().replace("\r\n", "\n"); String expected = """ - _:c14n2 . - _:c14n3 . - _:c14n0 _:c14n1 . - _:c14n2 _:c14n1 . - _:c14n3 _:c14n0 ."""; + _:c14n2 . + _:c14n3 . + _:c14n0 _:c14n1 . + _:c14n2 _:c14n1 . + _:c14n3 _:c14n0 ."""; assertEquals(expected, actual, "Canonical output should match expected format"); } @@ -317,10 +317,10 @@ void testSerializeFigure2() { String actual = canonicalOutput.trim().replace("\r\n", "\n"); String expected = """ - _:c14n0 . - _:c14n1 . - _:c14n0 . - _:c14n1 ."""; + _:c14n0 . + _:c14n1 . + _:c14n0 . + _:c14n1 ."""; assertEquals(expected, actual, "Canonical output should match RDFC-1.0 specification"); } From d2950e91160262f23aa28b6c390d9d925e8846cc Mon Sep 17 00:00:00 2001 From: Pierre Maillot Date: Mon, 15 Dec 2025 17:30:37 +0100 Subject: [PATCH 09/12] fix serializerFactory --- .../core/next/impl/common/util/IRIUtils.java | 4 - .../io/serialization/SerializerFactory.java | 66 ++-------------- .../base/AbstractGraphSerializer.java | 77 +++++++++++-------- .../rdfxml/RDFXMLSerializer.java | 21 +---- .../io/serialization/trig/TriGSerializer.java | 7 +- .../turtle/TurtleSerializer.java | 9 ++- .../serialization/SerializerFactoryTest.java | 46 ++++++++--- .../rdfxml/RDFXMLSerializerTest.java | 19 +++-- 8 files changed, 116 insertions(+), 133 deletions(-) diff --git a/src/main/java/fr/inria/corese/core/next/impl/common/util/IRIUtils.java b/src/main/java/fr/inria/corese/core/next/impl/common/util/IRIUtils.java index d5889fd09..5afe6d323 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/common/util/IRIUtils.java +++ b/src/main/java/fr/inria/corese/core/next/impl/common/util/IRIUtils.java @@ -17,8 +17,6 @@ */ public class IRIUtils { - private static final Logger logger = LoggerFactory.getLogger(IRIUtils.class); - // Example 1 : http://webisa.webdatacommons.org/data/sparql?query=q#line1 private static final Pattern IRI_PATTERN = Pattern.compile("^(?" + // http://webisa.webdatacommons.org "(?[\\w\\-]+):" + // http: @@ -50,13 +48,11 @@ private IRIUtils() { * @return the guessed namespace of the IRI or an empty string if no match is found. */ public static String guessNamespace(String iri) { - logger.info("guessNamespace {}", iri); if (isInvalidInput(iri)) { return ""; } try { Matcher matcher = matchWithTimeout(IRI_PATTERN, iri); - logger.info("{} : {}", iri, matcher.matches()); if (matcher == null || !matcher.matches()) { if (iri.endsWith("#")) { return iri; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java index a0183829d..86cae25b1 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java @@ -65,76 +65,22 @@ public SerializerFactory() { Map> tempRegistry = new HashMap<>(); Map> tempDefaultRegistry = new HashMap<>(); - tempRegistry.put(RDFFormat.TURTLE, (model, genericConfig) -> { - if (genericConfig instanceof TurtleSerializerOptions specificConfig) { - return new TurtleSerializer(model, specificConfig); - } - throw new SerializationException( - "Invalid configuration type. Expected TurtleSerializerOptions, got: " + - genericConfig.getClass().getSimpleName(), - RDFFormat.TURTLE.getName() - ); - }); + tempRegistry.put(RDFFormat.TURTLE, (model, genericConfig) -> new TurtleSerializer(model, genericConfig)); tempDefaultRegistry.put(RDFFormat.TURTLE, TurtleSerializer::new); - tempRegistry.put(RDFFormat.NTRIPLES, (model, genericConfig) -> { - if (genericConfig instanceof NTriplesSerializerOptions specificConfig) { - return new NTriplesSerializer(model, specificConfig); - } - throw new SerializationException( - "Invalid configuration type. Expected NTriplesSerializerOptions, got: " + - genericConfig.getClass().getSimpleName(), - RDFFormat.NTRIPLES.getName() - ); - }); + tempRegistry.put(RDFFormat.NTRIPLES, (model, genericConfig) -> new NTriplesSerializer(model, genericConfig)); tempDefaultRegistry.put(RDFFormat.NTRIPLES, NTriplesSerializer::new); - tempRegistry.put(RDFFormat.NQUADS, (model, genericConfig) -> { - if (genericConfig instanceof NQuadsSerializerOptions specificConfig) { - return new NQuadsSerializer(model, specificConfig); - } - throw new SerializationException( - "Invalid configuration type. Expected NQuadsSerializerOptions, got: " + - genericConfig.getClass().getSimpleName(), - RDFFormat.NQUADS.getName() - ); - }); + tempRegistry.put(RDFFormat.NQUADS, (model, genericConfig) -> new NQuadsSerializer(model, genericConfig)); tempDefaultRegistry.put(RDFFormat.NQUADS, NQuadsSerializer::new); - tempRegistry.put(RDFFormat.TRIG, (model, genericConfig) -> { - if (genericConfig instanceof TriGSerializerOptions specificConfig) { - return new TriGSerializer(model, specificConfig); - } - throw new SerializationException( - "Invalid configuration type. Expected TriGSerializerOptions, got: " + - genericConfig.getClass().getSimpleName(), - RDFFormat.TRIG.getName() - ); - }); + tempRegistry.put(RDFFormat.TRIG, (model, genericConfig) -> new TriGSerializer(model, genericConfig)); tempDefaultRegistry.put(RDFFormat.TRIG, TriGSerializer::new); - tempRegistry.put(RDFFormat.RDFXML, (model, genericConfig) -> { - if (genericConfig instanceof RDFXMLSerializerOptions specificConfig) { - return new RDFXMLSerializer(model, specificConfig); - } - throw new SerializationException( - "Invalid configuration type. Expected RDFXMLSerializerOption, got: " + - genericConfig.getClass().getSimpleName(), - RDFFormat.RDFXML.getName() - ); - }); + tempRegistry.put(RDFFormat.RDFXML, (model, genericConfig) -> new RDFXMLSerializer(model, genericConfig)); tempDefaultRegistry.put(RDFFormat.RDFXML, RDFXMLSerializer::new); - tempRegistry.put(RDFFormat.JSONLD, (model, genericConfig) -> { - if (genericConfig instanceof JSONLDOptions specificConfig) { - return new JSONLDSerializer(model, specificConfig); - } - throw new SerializationException( - "Invalid configuration type. Expected JSONLDOptions, got: " + - genericConfig.getClass().getSimpleName(), - RDFFormat.JSONLD.getName() - ); - }); + tempRegistry.put(RDFFormat.JSONLD, (model, genericConfig) -> new JSONLDSerializer(model, genericConfig)); tempDefaultRegistry.put(RDFFormat.JSONLD, JSONLDSerializer::new); tempRegistry.put(RDFFormat.RDFC_1_0, (model, genericConfig) -> { diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java index 66f1e60b3..6e247f9ca 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/base/AbstractGraphSerializer.java @@ -2,9 +2,8 @@ import fr.inria.corese.core.next.api.*; import fr.inria.corese.core.next.api.io.IOOptions; -import fr.inria.corese.core.next.api.io.serializer.PrettyPrintOptions; -import fr.inria.corese.core.next.api.io.serializer.UsesPrefixOptions; -import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; +import fr.inria.corese.core.next.api.io.common.BaseIRIOptions; +import fr.inria.corese.core.next.api.io.serializer.*; import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; import fr.inria.corese.core.next.impl.common.util.IRIUtils; import fr.inria.corese.core.next.impl.common.vocabulary.*; @@ -44,7 +43,7 @@ public abstract class AbstractGraphSerializer implements RDFSerializer { protected static final Logger logger = LoggerFactory.getLogger(AbstractGraphSerializer.class); protected final Model model; - protected AbstractSerializerOptions option; + protected IOOptions option; protected PrefixHandler prefixHandler; protected final Set consumedBlankNodes; protected final Set currentlyWritingBlankNodes; @@ -59,11 +58,7 @@ public abstract class AbstractGraphSerializer implements RDFSerializer { protected AbstractGraphSerializer(Model model, IOOptions config) { this.model = Objects.requireNonNull(model, "The model cannot be null"); Objects.requireNonNull(config, "The configuration cannot be null"); - if(config instanceof AbstractSerializerOptions abstractSerializerOptions) { - this.option = abstractSerializerOptions; - } else { - throw new IllegalArgumentException("AbstractGraphSerializer expect option object to extend AbstractSerializerOptions. Inheritor class should have taken care of that."); - } + this.option = config; if(config instanceof UsesPrefixOptions usesPrefixOptions) { this.prefixHandler = usesPrefixOptions.getPrefixHandler(); } else { @@ -114,10 +109,12 @@ public void write(Writer writer) throws SerializationException { * @throws IOException if an I/O error occurs. */ protected void writeHeader(Writer writer) throws IOException { - if (option.getBaseIRI() != null) { + if (option instanceof BaseIRIOptions baseIRIOptions + && option instanceof LineEndingOptions lineEndingOptions + && baseIRIOptions.getBaseIRI() != null) { writer.write(String.format("@base <%s> .%s", - option.getBaseIRI(), - option.getLineEnding())); + baseIRIOptions.getBaseIRI(), + lineEndingOptions.getLineEnding())); } Set actuallyUsedNamespaces = Set.of(); @@ -176,7 +173,9 @@ protected Set collectUsedNamespaces() { */ protected void writePrefixDeclarations(Writer writer, Set actuallyUsedNamespaces) throws IOException { if(this.option instanceof UsesPrefixOptions prefixOptions - && prefixOptions.usePrefixes()) { + && this.option instanceof LineEndingOptions lineEndingOptions + && this.option instanceof BaseIRIOptions baseIRIOptions + && prefixOptions.usePrefixes()) { List prefixes = new ArrayList<>(actuallyUsedNamespaces.stream().map(namespace -> this.prefixHandler.getPrefix(namespace)).toList()); if (prefixOptions.getPrefixOrdering() == PrefixOrderingEnum.ALPHABETICAL) { @@ -187,11 +186,11 @@ protected void writePrefixDeclarations(Writer writer, Set actuallyUsedNa writer.write(String.format("@prefix %s: <%s> .%s", prefix, this.prefixHandler.getNamespace(prefix), - option.getLineEnding())); + lineEndingOptions.getLineEnding())); } - if (!prefixes.isEmpty() || option.getBaseIRI() != null) { - writer.write(option.getLineEnding()); + if (!prefixes.isEmpty() || baseIRIOptions.getBaseIRI() != null) { + writer.write(lineEndingOptions.getLineEnding()); } } } @@ -217,7 +216,9 @@ protected void writeSimpleStatements(Writer writer) throws IOException { for (Statement stmt : model) { if (!isConsumed(stmt.getSubject())) { writeStatement(writer, stmt); - writer.write(option.getLineEnding()); + if(this.option instanceof LineEndingOptions lineEndingOptions) { + writer.write(lineEndingOptions.getLineEnding()); + } } } } @@ -337,7 +338,7 @@ protected void writeValue(Writer writer, Value value) throws IOException { * @throws IOException if an I/O error occurs. */ protected void writeIRI(Writer writer, IRI iri) throws IOException { - if (option.isStrictMode() && option.validateURIs()) { + if (this.option instanceof AbstractSerializerOptions abstractSerializerOptions && abstractSerializerOptions.isStrictMode() && abstractSerializerOptions.validateURIs()) { validateIRI(iri); } @@ -419,9 +420,10 @@ protected boolean shouldWriteDatatype(Literal literal) { return false; } - return option.getLiteralDatatypePolicy() == LiteralDatatypePolicyEnum.ALWAYS_TYPED || - (!datatype.equals(XSD.xsdString.getIRI()) && - option.getLiteralDatatypePolicy() == LiteralDatatypePolicyEnum.MINIMAL); + return this.option instanceof DatatypePolicyOptions datatypePolicyOptions && + (datatypePolicyOptions.getLiteralDatatypePolicy() == LiteralDatatypePolicyEnum.ALWAYS_TYPED + || (!datatype.equals(XSD.xsdString.getIRI()) && + datatypePolicyOptions.getLiteralDatatypePolicy() == LiteralDatatypePolicyEnum.MINIMAL)); } /** @@ -450,8 +452,10 @@ protected void writeInlineBlankNode(Writer writer, List properties) t } firstProperty = false; - if (this.option instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.prettyPrint()) { - writer.write(option.getLineEnding() + propIndent); + if (this.option instanceof PrettyPrintOptions prettyPrintOptions + && this.option instanceof LineEndingOptions lineEndingOptions + && prettyPrintOptions.prettyPrint()) { + writer.write(lineEndingOptions.getLineEnding() + propIndent); } else { writer.write(SerializationConstants.SPACE); } @@ -461,8 +465,11 @@ protected void writeInlineBlankNode(Writer writer, List properties) t writeValue(writer, stmt.getObject()); } - if (this.option instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.prettyPrint() && !properties.isEmpty() && !firstProperty) { - writer.write(option.getLineEnding() + currentIndent); + if (this.option instanceof PrettyPrintOptions prettyPrintOptions + && this.option instanceof LineEndingOptions lineEndingOptions + && prettyPrintOptions.prettyPrint() + && !properties.isEmpty() && !firstProperty) { + writer.write(lineEndingOptions.getLineEnding() + currentIndent); } writer.write(SerializationConstants.BLANK_NODE_END); @@ -501,8 +508,10 @@ protected void writeOptimizedStatements(Writer writer) throws IOException { for (Map.Entry> predicateEntry : byPredicate.entrySet()) { if (!firstPredicate) { writer.write(SerializationConstants.SEMICOLON); - if (this.option instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.prettyPrint()) { - writer.write(option.getLineEnding() + indent + prettyPrintOptions.getIndent()); + if (this.option instanceof PrettyPrintOptions prettyPrintOptions + && this.option instanceof LineEndingOptions lineEndingOptions + && prettyPrintOptions.prettyPrint()) { + writer.write(lineEndingOptions.getLineEnding() + indent + prettyPrintOptions.getIndent()); } else { writer.write(SerializationConstants.SPACE); } @@ -516,8 +525,10 @@ protected void writeOptimizedStatements(Writer writer) throws IOException { for (Statement stmt : predicateEntry.getValue()) { if (!firstObject) { writer.write(SerializationConstants.COMMA); - if (this.option instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.prettyPrint()) { - writer.write(option.getLineEnding() + indent + prettyPrintOptions.getIndent() + prettyPrintOptions.getIndent()); + if (this.option instanceof PrettyPrintOptions prettyPrintOptions + && this.option instanceof LineEndingOptions lineEndingOptions + && prettyPrintOptions.prettyPrint()) { + writer.write(lineEndingOptions.getLineEnding() + indent + prettyPrintOptions.getIndent() + prettyPrintOptions.getIndent()); } else { writer.write(SerializationConstants.SPACE); } @@ -529,7 +540,9 @@ protected void writeOptimizedStatements(Writer writer) throws IOException { } writer.write(SerializationConstants.SPACE + SerializationConstants.POINT); - writer.write(option.getLineEnding()); + if(this.option instanceof LineEndingOptions lineEndingOptions) { + writer.write(lineEndingOptions.getLineEnding()); + } } } @@ -867,7 +880,9 @@ protected void validateValue(Value value) { throw new SerializationException("Value cannot be null in {} format when strictMode is enabled.", getFormatName()); } - if (option.isStrictMode() && value.isLiteral()) { + if (this.option instanceof AbstractSerializerOptions abstractSerializerOptions + && abstractSerializerOptions.isStrictMode() + && value.isLiteral()) { validateLiteral((Literal) value); } } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java index 6ecd81b5b..5c037295d 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java @@ -45,8 +45,6 @@ */ public class RDFXMLSerializer implements RDFSerializer { - private static final Logger logger = LoggerFactory.getLogger(RDFXMLSerializer.class); - private final Model model; private final IOOptions config; private final PrefixHandler prefixHandler; @@ -181,17 +179,12 @@ private void writeRdfRootElement(Writer writer) throws IOException { * @throws IOException if an I/O error occurs. */ private void writeNamespaceAttributes(Writer writer, Set actuallyUsedNamespaces) throws IOException { - logger.info("actually used Namespaces: {}", actuallyUsedNamespaces); - logger.info("Known prefixes: {}", this.prefixHandler.getPrefixMap()); - logger.info("Known namespaces: {}", this.prefixHandler.getNamespaceMap()); ArrayList namespacelist = new ArrayList<>(actuallyUsedNamespaces); if(this.config instanceof UsesPrefixOptions usesPrefixOptions && usesPrefixOptions.autoDeclarePrefixes()) { - logger.info("{}", namespacelist); namespacelist.forEach(namespace -> { - logger.info("{} is in PrefixHandler = {}", namespace, this.prefixHandler.hasNamespace(namespace)); if (! this.prefixHandler.hasNamespace(namespace)) { String prefix = getSuggestedPrefix(namespace); if (prefix != null) { @@ -204,10 +197,8 @@ private void writeNamespaceAttributes(Writer writer, Set actuallyUsedNam if (this.config instanceof PrettyPrintOptions prettyPrintOptions && prettyPrintOptions.getPrefixOrdering() == PrefixOrderingEnum.ALPHABETICAL) { namespacelist.sort( - (ns1, ns2) -> { - logger.info("{}: {} <> {}: {}", ns1, prefixHandler.getPrefix(ns1), ns2, prefixHandler.getPrefix(ns2)); - return prefixHandler.getPrefix(ns1).compareTo(prefixHandler.getPrefix(ns2)); - } + (ns1, ns2) -> + prefixHandler.getPrefix(ns1).compareTo(prefixHandler.getPrefix(ns2)) ); } @@ -243,7 +234,6 @@ private Set collectUsedNamespaces() { .filter(Value::isIRI) .map(v -> IRIUtils.guessNamespace(v.stringValue())) .collect(Collectors.toSet()); - logger.info("Potential namespaces{}", potentialNamespaces); return potentialNamespaces; } @@ -257,17 +247,13 @@ private Set collectUsedNamespaces() { * @return The prefixed name (e.g., "foaf:name") or null if no suitable prefix is found. */ private String getPrefixedNameInternal(String iriString) { - logger.info("getPrefixedNameInternal({})", iriString); String longestMatchingNamespace = null; String correspondingPrefix = null; int longestMatchLength = -1; - logger.info("{}", this.prefixHandler.getPrefixMap()); for (String namespace : this.prefixHandler.getNamespaces()) { - logger.info("{} contains {} ? {}", iriString, namespace, iriString.startsWith(namespace)); if (iriString.startsWith(namespace)) { String prefix = this.prefixHandler.getPrefix(namespace); - logger.info("{} = {}", namespace, prefix); if (namespace.length() > longestMatchLength) { longestMatchLength = namespace.length(); longestMatchingNamespace = namespace; @@ -278,7 +264,6 @@ private String getPrefixedNameInternal(String iriString) { if (longestMatchingNamespace != null) { String localName = iriString.substring(longestMatchingNamespace.length()); - logger.info("{} = {} : {}", iriString, longestMatchingNamespace, localName); if (localName.isEmpty()) { return correspondingPrefix + SerializationConstants.COLON; } @@ -368,7 +353,6 @@ private void writePropertyElement(Writer writer, IRI predicate, Value object, St elementName = prefixedPredicateName; } else { elementName = predicateString; - logger.warn("Predicate IRI '{}' cannot be expressed as a valid prefixed element name. Using full IRI as element name in RDF/XML.", predicateString); } writer.write(currentIndent); @@ -481,7 +465,6 @@ private String getSuggestedPrefix(String namespace) { base = "p"; } } catch (java.net.URISyntaxException e) { - logger.warn("Malformed URI encountered while suggesting prefix: {}", namespace, e); base = "p"; } } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializer.java index 8d692d5af..a3f7cfab9 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/trig/TriGSerializer.java @@ -19,6 +19,7 @@ import fr.inria.corese.core.next.api.io.serializer.LineEndingOptions; import fr.inria.corese.core.next.api.io.serializer.PrettyPrintOptions; import fr.inria.corese.core.next.impl.io.serialization.base.AbstractGraphSerializer; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.option.AbstractTFamilyOptions; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; @@ -247,7 +248,11 @@ protected String escapeLiteralString(String value) { sb.append(SerializationConstants.BACK_SLASH).append(SerializationConstants.BACK_SLASH); break; default: - if (option.escapeUnicode() && (c <= 0x1F || c == 0x7F || (c >= 0x80 && c <= 0xFFFF))) { + if (this.option instanceof AbstractSerializerOptions abstractSerializerOptions + && abstractSerializerOptions.escapeUnicode() + && (c <= 0x1F + || c == 0x7F + || (c >= 0x80 && c <= 0xFFFF))) { sb.append(String.format("\\u%04X", (int) c)); } else if (Character.isHighSurrogate(c)) { int codePoint = value.codePointAt(i); diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializer.java index 4de909266..14f5115ef 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/turtle/TurtleSerializer.java @@ -6,6 +6,7 @@ import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.IOOptions; +import fr.inria.corese.core.next.impl.io.serialization.option.AbstractSerializerOptions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -152,12 +153,16 @@ protected String escapeLiteralString(String value) { if (Character.isISOControl(c) ||c == 0x7F) { sb.append(String.format("\\u%04X", (int) c)); } - else if (option.escapeUnicode() && c >= 0x80 && c <= 0xFFFF) { + else if (this.option instanceof AbstractSerializerOptions abstractSerializerOptions + && abstractSerializerOptions.escapeUnicode() + && c >= 0x80 + && c <= 0xFFFF) { sb.append(String.format("\\u%04X", (int) c)); } else if (Character.isHighSurrogate(c)) { int codePoint = value.codePointAt(i); if (Character.isValidCodePoint(codePoint)) { - if (option.escapeUnicode()) { + if (this.option instanceof AbstractSerializerOptions abstractSerializerOptions + && abstractSerializerOptions.escapeUnicode()) { sb.append(String.format("\\U%08X", codePoint)); } else { sb.append(Character.toChars(codePoint)); diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactoryTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactoryTest.java index 196d32d63..d23d6165d 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactoryTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactoryTest.java @@ -141,16 +141,6 @@ void createSerializer_shouldReturnJSONLDSerializer_forJSONLDFormat() { } } - @Test - @DisplayName("createSerializer should throw SerializationException for wrong config type") - void createSerializer_shouldThrowSerializationException_forWrongConfigType() { - IOOptions wrongConfig = mock(IOOptions.class); - - assertThrows(SerializationException.class, - () -> factory.createSerializer(RDFFormat.TURTLE, mockModel, wrongConfig), - "Should throw SerializationException for wrong config type"); - } - @Test @DisplayName("createSerializer should throw NullPointerException for a null format") void createSerializer_shouldThrowNPE_forNullFormat() { @@ -283,4 +273,40 @@ void createSerializerWithoutConfig_shouldThrowNPE_forNullModel() { () -> factory.createSerializer(RDFFormat.TURTLE, null), "Should throw NullPointerException for null Model"); } + + @Test + @DisplayName("Should accept cross-use of config objects") + void configCrossUse() { + JSONLDOptions jsonldOptions = new JSONLDOptions.Builder().build(); + NQuadsSerializerOptions nQuadsSerializerOptions = new NQuadsSerializerOptions.Builder().build(); + NTriplesSerializerOptions nTriplesSerializerOptions = new NTriplesSerializerOptions.Builder().build(); + RDFXMLSerializerOptions rdfxmlSerializerOptions = new RDFXMLSerializerOptions.Builder().build(); + TriGSerializerOptions triGSerializerOptions = new TriGSerializerOptions.Builder().build(); + TurtleSerializerOptions turtleSerializerOptions = new TurtleSerializerOptions.Builder().build(); + + assertDoesNotThrow(() -> { + // JSONLDOptions -- NQuads + factory.createSerializer(RDFFormat.JSONLD, mockModel, nQuadsSerializerOptions); + }); + assertDoesNotThrow(() -> { + // NQuads -- NTriples + factory.createSerializer(RDFFormat.NQUADS, mockModel, nTriplesSerializerOptions); + }); + assertDoesNotThrow(() -> { + // NTriples -- RDFXML + factory.createSerializer(RDFFormat.NTRIPLES, mockModel, rdfxmlSerializerOptions); + }); + assertDoesNotThrow(() -> { + // RDFXML -- TriG + factory.createSerializer(RDFFormat.RDFXML, mockModel, triGSerializerOptions); + }); + assertDoesNotThrow(() -> { + // TriG -- Turtle + factory.createSerializer(RDFFormat.TRIG, mockModel, turtleSerializerOptions); + }); + assertDoesNotThrow(() -> { + // Turtle -- JSONLD + factory.createSerializer(RDFFormat.TURTLE, mockModel, jsonldOptions); + }); + } } \ No newline at end of file diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java index 4a3cbe020..e05250020 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializerTest.java @@ -2,6 +2,7 @@ import fr.inria.corese.core.next.api.Model; import fr.inria.corese.core.next.api.Statement; +import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; import fr.inria.corese.core.next.impl.common.vocabulary.XSD; import fr.inria.corese.core.next.impl.exception.SerializationException; @@ -20,8 +21,7 @@ import java.io.StringWriter; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.when; /** @@ -29,8 +29,6 @@ */ class RDFXMLSerializerTest { - private static final Logger logger = LoggerFactory.getLogger(RDFXMLSerializerTest.class); - @Mock private Model mockModel; RDFXMLSerializerOptions mockConfig; @@ -201,8 +199,6 @@ void shouldSerializeLiteralWithCustomDatatypeMinimalPolicy() throws Serializatio .prefixOrdering(PrefixOrderingEnum.ALPHABETICAL) .build(); - logger.info("AutoDeclarePrefix ? {}", testConfig.autoDeclarePrefixes()); - RDFXMLSerializer serializer = new RDFXMLSerializer(mockModel, testConfig); serializer.write(writer); @@ -522,4 +518,15 @@ void shouldHandleEmptyModel() throws SerializationException { assertEquals(expected, writer.toString()); } + + @Test + @DisplayName("Should accept any IOOptions object") + void shouldAcceptAnyOptionFile() { + assertDoesNotThrow(() -> { + IOOptions option = new IOOptions() { + }; + when(mockModel.stream()).thenReturn(Stream.empty()); + RDFXMLSerializer serializer = new RDFXMLSerializer(mockModel, option); + }); + } } From 29b637982d25117f626152c29b88feeb9eb83b90 Mon Sep 17 00:00:00 2001 From: Pierre Maillot Date: Tue, 16 Dec 2025 14:49:47 +0100 Subject: [PATCH 10/12] Adding comments --- .../api/io/serializer/BlankNodeIdGenerationOptions.java | 3 +++ .../core/next/api/io/serializer/DatatypePolicyOptions.java | 3 +++ .../core/next/api/io/serializer/LineEndingOptions.java | 7 +++++++ .../core/next/api/io/serializer/PrettyPrintOptions.java | 3 +++ .../core/next/api/io/serializer/UsesPrefixOptions.java | 3 +++ .../corese/core/next/impl/common/prefix/PrefixHandler.java | 3 +++ .../io/serialization/option/AbstractSerializerOptions.java | 1 + .../io/serialization/option/AbstractTFamilyOptions.java | 4 +++- .../io/serialization/rdfxml/RDFXMLSerializerOptions.java | 4 ++++ 9 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serializer/BlankNodeIdGenerationOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serializer/BlankNodeIdGenerationOptions.java index 91842391b..5fe2d6379 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serializer/BlankNodeIdGenerationOptions.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serializer/BlankNodeIdGenerationOptions.java @@ -1,5 +1,8 @@ package fr.inria.corese.core.next.api.io.serializer; +/** + * Interface for options that determine the generation of blank node Ids for serializers. + */ public interface BlankNodeIdGenerationOptions { /** diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serializer/DatatypePolicyOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serializer/DatatypePolicyOptions.java index 0f55c34a2..b21dcaa48 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serializer/DatatypePolicyOptions.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serializer/DatatypePolicyOptions.java @@ -2,6 +2,9 @@ import fr.inria.corese.core.next.impl.io.serialization.option.LiteralDatatypePolicyEnum; +/** + * Interface for serializer options to determine the policy for the literal datatypes + */ public interface DatatypePolicyOptions { diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serializer/LineEndingOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serializer/LineEndingOptions.java index 64b226796..2b0478b2a 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serializer/LineEndingOptions.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serializer/LineEndingOptions.java @@ -1,6 +1,13 @@ package fr.inria.corese.core.next.api.io.serializer; +/** + * Interface to specify which line ending a serializer must use. + */ public interface LineEndingOptions { + /** + * + * @return the end line characters + */ String getLineEnding(); } diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serializer/PrettyPrintOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serializer/PrettyPrintOptions.java index 6ad5685af..ad371aae0 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serializer/PrettyPrintOptions.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serializer/PrettyPrintOptions.java @@ -2,6 +2,9 @@ import fr.inria.corese.core.next.impl.io.serialization.option.PrefixOrderingEnum; +/** + * Interface for the options of serializer allowing for pretty printing. + */ public interface PrettyPrintOptions { /** diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serializer/UsesPrefixOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/serializer/UsesPrefixOptions.java index fd0bb51f9..5059bb46f 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serializer/UsesPrefixOptions.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serializer/UsesPrefixOptions.java @@ -3,6 +3,9 @@ import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; import fr.inria.corese.core.next.impl.io.serialization.option.PrefixOrderingEnum; +/** + * Interface for the options of serializer that can declare prefixes + */ public interface UsesPrefixOptions { /** diff --git a/src/main/java/fr/inria/corese/core/next/impl/common/prefix/PrefixHandler.java b/src/main/java/fr/inria/corese/core/next/impl/common/prefix/PrefixHandler.java index 402457f60..9e2722fcd 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/common/prefix/PrefixHandler.java +++ b/src/main/java/fr/inria/corese/core/next/impl/common/prefix/PrefixHandler.java @@ -45,6 +45,9 @@ public PrefixHandler(boolean includeStandardVocabularies) { } } + /** + * Copy constructor + */ public PrefixHandler(PrefixHandler oHandler) { this.prefixToNamespace = new ConcurrentHashMap<>(oHandler.prefixToNamespace); this.namespaceToPrefix = new ConcurrentHashMap<>(oHandler.namespaceToPrefix); diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java index 3e137c881..9a971efa1 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractSerializerOptions.java @@ -132,6 +132,7 @@ public String getBaseIRI() { * * @return {@code true} if stable blank node IDs are enabled, {@code false} otherwise. */ + @Override public boolean stableBlankNodeIds() { return stableBlankNodeIds; } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java index c1ed6d9b1..8bf85d17e 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/option/AbstractTFamilyOptions.java @@ -307,7 +307,9 @@ public abstract static class AbstractTFamilyBuilder Date: Wed, 17 Dec 2025 13:16:20 +0100 Subject: [PATCH 11/12] Removed unused --- .../core/next/impl/common/util/IRIUtils.java | 3 --- .../io/serialization/SerializerFactory.java | 6 ------ .../serialization/rdfxml/RDFXMLSerializer.java | 18 ------------------ 3 files changed, 27 deletions(-) diff --git a/src/main/java/fr/inria/corese/core/next/impl/common/util/IRIUtils.java b/src/main/java/fr/inria/corese/core/next/impl/common/util/IRIUtils.java index 5afe6d323..edf0c3c6c 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/common/util/IRIUtils.java +++ b/src/main/java/fr/inria/corese/core/next/impl/common/util/IRIUtils.java @@ -1,8 +1,5 @@ package fr.inria.corese.core.next.impl.common.util; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.net.URI; import java.net.URISyntaxException; import java.util.Set; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java index 86cae25b1..8693e1d5e 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/SerializerFactory.java @@ -6,21 +6,15 @@ import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.api.io.serializer.RDFSerializer; import fr.inria.corese.core.next.impl.exception.SerializationException; -import fr.inria.corese.core.next.impl.io.common.JSONLDOptions; import fr.inria.corese.core.next.impl.io.serialization.canonical.RDFC10Canonicalizer; import fr.inria.corese.core.next.impl.io.serialization.canonical.RDFC10Serializer; import fr.inria.corese.core.next.impl.io.serialization.canonical.RDFC10SerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.jsonld.JSONLDSerializer; import fr.inria.corese.core.next.impl.io.serialization.nquads.NQuadsSerializer; -import fr.inria.corese.core.next.impl.io.serialization.nquads.NQuadsSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.ntriples.NTriplesSerializer; -import fr.inria.corese.core.next.impl.io.serialization.ntriples.NTriplesSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.rdfxml.RDFXMLSerializer; -import fr.inria.corese.core.next.impl.io.serialization.rdfxml.RDFXMLSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.trig.TriGSerializer; -import fr.inria.corese.core.next.impl.io.serialization.trig.TriGSerializerOptions; import fr.inria.corese.core.next.impl.io.serialization.turtle.TurtleSerializer; -import fr.inria.corese.core.next.impl.io.serialization.turtle.TurtleSerializerOptions; import fr.inria.corese.core.next.impl.temp.CoreseAdaptedValueFactory; import java.util.Collections; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java index 5c037295d..d70917a06 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/RDFXMLSerializer.java @@ -13,8 +13,6 @@ import fr.inria.corese.core.next.impl.common.prefix.PrefixHandler; import fr.inria.corese.core.next.impl.common.util.IRIUtils; import fr.inria.corese.core.next.impl.common.vocabulary.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import fr.inria.corese.core.next.api.IRI; import fr.inria.corese.core.next.api.Literal; @@ -272,22 +270,6 @@ private String getPrefixedNameInternal(String iriString) { return null; } - /** - * Generates a unique prefix based on a given base string, ensuring it's not already in use. - * This method appends numbers to the base prefix until a unique one is found. - * - * @param basePrefix The desired base prefix (e.g., "foaf"). - * @return A unique prefix (e.g., "foaf", "foaf1", "foaf2"). - */ - private String generateUniquePrefix(String basePrefix) { - String candidate = basePrefix; - int i = 0; - while (this.prefixHandler.hasPrefix(candidate)) { - candidate = basePrefix + (++i); - } - return candidate; - } - /** * Writes an `` element for a given subject. * This element contains all properties (predicates and objects) for that subject. From 69855ea86d3b741b8c8b76edcb801d641a7e808e Mon Sep 17 00:00:00 2001 From: "AD\\aabdoun" Date: Wed, 17 Dec 2025 13:51:03 +0100 Subject: [PATCH 12/12] Imports are never used --- .../fr/inria/corese/core/next/api/io/common/BaseIRIOptions.java | 1 - .../inria/corese/core/next/api/io/serializer/RDFSerializer.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/fr/inria/corese/core/next/api/io/common/BaseIRIOptions.java b/src/main/java/fr/inria/corese/core/next/api/io/common/BaseIRIOptions.java index add748be1..9205cfd14 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/common/BaseIRIOptions.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/common/BaseIRIOptions.java @@ -1,6 +1,5 @@ package fr.inria.corese.core.next.api.io.common; -import fr.inria.corese.core.next.api.io.IOOptions; /** * Options for RDF parsers and serializers that support a base IRI. diff --git a/src/main/java/fr/inria/corese/core/next/api/io/serializer/RDFSerializer.java b/src/main/java/fr/inria/corese/core/next/api/io/serializer/RDFSerializer.java index 5e4868522..5fd4a2da4 100644 --- a/src/main/java/fr/inria/corese/core/next/api/io/serializer/RDFSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/api/io/serializer/RDFSerializer.java @@ -37,7 +37,7 @@ public interface RDFSerializer { */ default String getFormatName() { return getRDFFormat().getName(); - }; + } /** * Gets the RDF format that this serializer generates.