From adff4a5950c93b0b8301f1ad86e6762dcf0bb942 Mon Sep 17 00:00:00 2001 From: "AD\\aabdoun" Date: Mon, 30 Jun 2025 15:41:33 +0200 Subject: [PATCH 01/15] =?UTF-8?q?l=E2=80=99issue=2088=20(parseur=20NTriple?= =?UTF-8?q?s/NQuads)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/io/parser/nquads/NQuadsParser.java | 259 ++++++++++++++++++ .../io/parser/ntriples/NTriplesParser.java | 179 ++++++++++++ .../io/parser/nquads/NQuadsParserTest.java | 151 ++++++++++ .../parser/ntriples/NTriplesParserTest.java | 258 +++++++++++++++++ 4 files changed, 847 insertions(+) create mode 100644 src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParser.java create mode 100644 src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParser.java create mode 100644 src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParserTest.java create mode 100644 src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParserTest.java diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParser.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParser.java new file mode 100644 index 000000000..5f2d95184 --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParser.java @@ -0,0 +1,259 @@ +package fr.inria.corese.core.next.impl.io.parser.nquads; + +import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.base.io.RdfFormat; +import fr.inria.corese.core.next.api.base.io.parser.AbstractParser; +import fr.inria.corese.core.next.api.io.IOConfig; +import fr.inria.corese.core.next.impl.exception.ParsingErrorException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * A simplified parser for N-Quads format. + * This parser reads N-Quads line by line and attempts to parse + * subject, predicate, object, and an optional graph, then adds them to the model. + * + * IMPORTANT LIMITATION: This is a basic implementation. The `splitNqLine` method + * is simplistic and WILL NOT correctly handle complex RDF terms, especially literals + * containing spaces or intricate escaping, when trying to differentiate the object from the graph. + * For production use, a more robust N-Quads parsing library would be highly recommended, + * ideally one that uses a proper state machine or lexer. + */ +public class NQuadsParser extends AbstractParser { + + private static final Logger logger = LoggerFactory.getLogger(NQuadsParser.class); + + public NQuadsParser(Model model, ValueFactory factory) { + super(model, factory); + } + + public NQuadsParser(Model model, ValueFactory factory, IOConfig config) { + super(model, factory); + setConfig(config); + } + + @Override + public RdfFormat getRDFFormat() { + return RdfFormat.NQUADS; + } + + @Override + public void parse(InputStream in, String baseURI) throws ParsingErrorException { + parse(new InputStreamReader(in, StandardCharsets.UTF_8), baseURI); + } + + @Override + public void parse(Reader reader, String baseURI) throws ParsingErrorException { + try (BufferedReader bufferedReader = new BufferedReader(reader)) { + String line; + int lineNumber = 0; + while ((line = bufferedReader.readLine()) != null) { + lineNumber++; + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) { + continue; + } + + + if (!line.endsWith(".")) { + logger.warn("Line {} does not end with a period and will be skipped: {}", lineNumber, line); + + continue; + } + + + line = line.substring(0, line.length() - 1).trim(); + + processLine(line, lineNumber); + } + } catch (Exception e) { + if (e instanceof ParsingErrorException) { + throw (ParsingErrorException) e; + } + throw new ParsingErrorException("Error reading N-Quads input: " + e.getMessage(), e); + } + } + + /** + * Processes a single N-Quads line, parsing its components and adding them to the model. + * This method attempts to differentiate between N-Triples (default graph) and N-Quads (named graph). + * + * @param line The N-Quads line to process. + * @param lineNumber The line number for error reporting. + * @throws ParsingErrorException if the line cannot be parsed according to N-Quads syntax. + */ + private void processLine(String line, int lineNumber) throws ParsingErrorException { + try { + String[] parts = splitNqLine(line); + + if (parts.length < 3) { + logger.error("Invalid N-Quads line (less than 3 parts) at line {}: {}", lineNumber, line); + throw new ParsingErrorException("Invalid N-Quads line at line " + lineNumber + ": " + line); + } + if (parts.length > 4) { + logger.warn("Line {} has more than 4 parts after initial split. This likely indicates a literal with spaces that was not handled correctly by the simple parser: {}", lineNumber, line); + throw new ParsingErrorException("Invalid N-Quads line (too many parts, likely literal parsing issue) at line " + lineNumber + ": " + line); + } + + Resource subject = parseResource(parts[0]); + IRI predicate = parseIRI(parts[1]); + Value object = parseValue(parts[2]); + + Resource graph = null; + if (parts.length == 4) { + graph = parseResource(parts[3]); + } + + + if (graph != null) { + getModel().add(subject, predicate, object, graph); + } else { + + getModel().add(subject, predicate, object); + } + + } catch (IllegalArgumentException e) { + throw new ParsingErrorException("Error parsing N-Quads line " + lineNumber + ": " + line, e); + } catch (Exception e) { + throw new ParsingErrorException("Unexpected error processing N-Quads line " + lineNumber + ": " + line, e); + } + } + + /** + * A very simplistic split for N-Quads. It tries to identify the main components. + * This will fail for complex literals containing spaces or quoted parts within, + * especially when a graph IRI is also present. + * A more robust parser would use a proper state machine or regex for parsing RDF terms. + * It attempts to differentiate between a 3-part triple (default graph) and a 4-part quad (named graph). + */ + private String[] splitNqLine(String line) { + + + List parts = new ArrayList<>(); + int currentPos = 0; + + for (int i = 0; i < 4; i++) { + line = line.substring(currentPos).trim(); + if (line.isEmpty()) break; + + String term; + if (line.startsWith("<")) { + int endIndex = line.indexOf('>'); + if (endIndex == -1) throw new IllegalArgumentException("Unclosed IRI: " + line); + term = line.substring(0, endIndex + 1); + currentPos = term.length(); + } else if (line.startsWith("_:")) { + int endIndex = line.indexOf(' '); + if (endIndex == -1) endIndex = line.length(); + term = line.substring(0, endIndex); + currentPos = term.length(); + } else if (line.startsWith("\"")) { + int firstQuote = line.indexOf('"'); + int lastQuote = -1; + + boolean inEscape = false; + for (int j = firstQuote + 1; j < line.length(); j++) { + char c = line.charAt(j); + if (c == '\\' && !inEscape) { + inEscape = true; + } else if (c == '"' && !inEscape) { + lastQuote = j; + break; + } else { + inEscape = false; + } + } + + if (lastQuote == -1) throw new IllegalArgumentException("Unclosed literal: " + line); + term = line.substring(0, lastQuote + 1); + currentPos = term.length(); + + + String remainder = line.substring(lastQuote + 1).trim(); + if (remainder.startsWith("^^<")) { + int dtEndIndex = remainder.indexOf('>'); + if (dtEndIndex == -1) throw new IllegalArgumentException("Unclosed datatype URI: " + remainder); + term += remainder.substring(0, dtEndIndex + 1); + currentPos += (dtEndIndex + 1) + (remainder.length() - remainder.trim().length()); + } else if (remainder.startsWith("@")) { + int langEndIndex = remainder.indexOf(' '); + if (langEndIndex == -1) langEndIndex = remainder.length(); + term += remainder.substring(0, langEndIndex); + currentPos += (langEndIndex) + (remainder.length() - remainder.trim().length()); + } + } else { + + throw new IllegalArgumentException("Malformed RDF term: " + line); + } + + parts.add(term.trim()); + line = line.substring(currentPos); + currentPos = 0; + } + + return parts.toArray(new String[0]); + } + + + /** + * Parses an N-Triples/N-Quads resource part (IRI or Blank Node). + */ + private Resource parseResource(String part) { + if (part.startsWith("<") && part.endsWith(">")) { + return getValueFactory().createIRI(part.substring(1, part.length() - 1)); + } else if (part.startsWith("_:")) { + return getValueFactory().createBNode(part.substring(2)); + } + throw new IllegalArgumentException("Invalid N-Quads resource: " + part); + } + + /** + * Parses an N-Triples/N-Quads IRI part. + */ + private IRI parseIRI(String part) { + if (part.startsWith("<") && part.endsWith(">")) { + return getValueFactory().createIRI(part.substring(1, part.length() - 1)); + } + throw new IllegalArgumentException("Invalid N-Quads IRI: " + part); + } + + /** + * Parses an N-Triples/N-Quads value part (IRI, Blank Node, or Literal). + * This is a highly simplified literal parser and does not handle complex escaping well. + */ + private Value parseValue(String part) { + if (part.startsWith("<") && part.endsWith(">")) { + return getValueFactory().createIRI(part.substring(1, part.length() - 1)); + } else if (part.startsWith("_:")) { + return getValueFactory().createBNode(part.substring(2)); + } else if (part.startsWith("\"")) { + int lastQuote = part.lastIndexOf('"'); + if (lastQuote == -1 || lastQuote == 0) { + throw new IllegalArgumentException("Invalid N-Quads literal: " + part); + } + String literalValue = part.substring(1, lastQuote); + + String remainder = part.substring(lastQuote + 1); + + if (remainder.startsWith("^^<") && remainder.endsWith(">")) { + String datatypeUri = remainder.substring(3, remainder.length() - 1); + return getValueFactory().createLiteral(literalValue, getValueFactory().createIRI(datatypeUri)); + } else if (remainder.startsWith("@")) { + String lang = remainder.substring(1); + return getValueFactory().createLiteral(literalValue, lang); + } else if (remainder.isEmpty()) { + return getValueFactory().createLiteral(literalValue); + } + throw new IllegalArgumentException("Invalid N-Quads literal format: " + part); + } + throw new IllegalArgumentException("Invalid N-Quads value: " + part); + } +} \ No newline at end of file diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParser.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParser.java new file mode 100644 index 000000000..55f020cd1 --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParser.java @@ -0,0 +1,179 @@ +package fr.inria.corese.core.next.impl.io.parser.ntriples; + +import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.base.io.RdfFormat; +import fr.inria.corese.core.next.api.base.io.parser.AbstractParser; +import fr.inria.corese.core.next.api.io.IOConfig; +import fr.inria.corese.core.next.impl.exception.ParsingErrorException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; + +/** + * A simplified parser for N-Triples format. + * This parser reads N-Triples line by line and attempts to parse + * subject, predicate, and object, then adds them to the model. + * It's a basic implementation and might not handle all edge cases or + * complex RDF term structures (like intricate literal escaping) perfectly. + * For production use, a more robust N-Triples parsing library would be recommended. + */ +public class NTriplesParser extends AbstractParser { + + private static final Logger logger = LoggerFactory.getLogger(NTriplesParser.class); + + public NTriplesParser(Model model, ValueFactory factory) { + super(model, factory); + } + + public NTriplesParser(Model model, ValueFactory factory, IOConfig config) { + super(model, factory); + setConfig(config); + } + + @Override + public RdfFormat getRDFFormat() { + return RdfFormat.NTRIPLES; + } + + @Override + public void parse(InputStream in, String baseURI) throws ParsingErrorException { + parse(new InputStreamReader(in, StandardCharsets.UTF_8), baseURI); + } + + @Override + public void parse(Reader reader, String baseURI) throws ParsingErrorException { + try (BufferedReader bufferedReader = new BufferedReader(reader)) { + String line; + int lineNumber = 0; + while ((line = bufferedReader.readLine()) != null) { + lineNumber++; + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) { + continue; + } + + if (!line.endsWith(".")) { + logger.warn("Line {} does not end with a period: {}", lineNumber, line); + } + + line = line.substring(0, line.length() - 1).trim(); + + processLine(line, lineNumber); + + } + } catch (Exception e) { + + if (e instanceof ParsingErrorException) { + throw (ParsingErrorException) e; + } + throw new ParsingErrorException("Error reading N-Triples input: " + e.getMessage(), e); + } + } + + /** + * Processes a single N-Triples line, parsing its components and adding them to the model. + * + * @param line The N-Triples line to process. + * @param lineNumber The line number for error reporting. + * @throws ParsingErrorException if the line cannot be parsed according to N-Triples syntax. + */ + private void processLine(String line, int lineNumber) throws ParsingErrorException { + try { + String[] parts = splitNtLine(line); + + if (parts.length < 3) { + logger.error("Invalid N-Triples line (less than 3 parts) at line {}: {}", lineNumber, line); + throw new ParsingErrorException("Invalid N-Triples line at line " + lineNumber + ": " + line); + } + + Resource subject = parseResource(parts[0]); + IRI predicate = parseIRI(parts[1]); + Value object = parseValue(parts[2]); + + getModel().add(subject, predicate, object); + + } catch (IllegalArgumentException e) { + throw new ParsingErrorException("Error parsing N-Triples line " + lineNumber + ": " + line, e); + } catch (Exception e) { + throw new ParsingErrorException("Unexpected error processing N-Triples line " + lineNumber + ": " + line, e); + } + } + + /** + * A very simplistic split for N-Triples. It tries to identify the main components. + * This will fail for complex literals containing spaces or quoted parts within. + * A more robust parser would use a proper state machine or regex for parsing RDF terms. + */ + private String[] splitNtLine(String line) { + int firstSpace = line.indexOf(' '); + if (firstSpace == -1) return new String[0]; + String subjectPart = line.substring(0, firstSpace).trim(); + String remaining = line.substring(firstSpace).trim(); + + int secondSpace = remaining.indexOf(' '); + if (secondSpace == -1) return new String[0]; + String predicatePart = remaining.substring(0, secondSpace).trim(); + String objectPart = remaining.substring(secondSpace).trim(); + + return new String[]{subjectPart, predicatePart, objectPart}; + } + + /** + * Parses an N-Triples resource part (IRI or Blank Node). + */ + private Resource parseResource(String part) { + if (part.startsWith("<") && part.endsWith(">")) { + return getValueFactory().createIRI(part.substring(1, part.length() - 1)); + } else if (part.startsWith("_:")) { + return getValueFactory().createBNode(part.substring(2)); + } + throw new IllegalArgumentException("Invalid N-Triples resource: " + part); + } + + /** + * Parses an N-Triples IRI part. + */ + private IRI parseIRI(String part) { + if (part.startsWith("<") && part.endsWith(">")) { + return getValueFactory().createIRI(part.substring(1, part.length() - 1)); + } + throw new IllegalArgumentException("Invalid N-Triples IRI: " + part); + } + + /** + * Parses an N-Triples value part (IRI, Blank Node, or Literal). + * This is a highly simplified literal parser. + */ + private Value parseValue(String part) { + if (part.startsWith("<") && part.endsWith(">")) { + return getValueFactory().createIRI(part.substring(1, part.length() - 1)); + } else if (part.startsWith("_:")) { + return getValueFactory().createBNode(part.substring(2)); + } else if (part.startsWith("\"")) { + int lastQuote = part.lastIndexOf('"'); + if (lastQuote == -1 || lastQuote == 0) { + throw new IllegalArgumentException("Invalid N-Triples literal: " + part); + } + String literalValue = part.substring(1, lastQuote); + + String remainder = part.substring(lastQuote + 1); + + if (remainder.startsWith("^^<") && remainder.endsWith(">")) { + String datatypeUri = remainder.substring(3, remainder.length() - 1); + return getValueFactory().createLiteral(literalValue, getValueFactory().createIRI(datatypeUri)); + } else if (remainder.startsWith("@")) { + String lang = remainder.substring(1); + return getValueFactory().createLiteral(literalValue, lang); + } else if (remainder.isEmpty()) { + return getValueFactory().createLiteral(literalValue); + } + throw new IllegalArgumentException("Invalid N-Triples literal format: " + part); + } + throw new IllegalArgumentException("Invalid N-Triples value: " + part); + } +} diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParserTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParserTest.java new file mode 100644 index 000000000..b793184c3 --- /dev/null +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParserTest.java @@ -0,0 +1,151 @@ +package fr.inria.corese.core.next.impl.io.parser.nquads; + + +import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.base.io.RdfFormat; +import fr.inria.corese.core.next.impl.exception.ParsingErrorException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.Reader; +import java.io.StringReader; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class NQuadsParserTest { + + @Mock + private Model mockModel; + + @Mock + private ValueFactory mockValueFactory; + + private NQuadsParser parser; + + + @Mock private IRI mockSubjectIRI; + @Mock private IRI mockPredicateIRI; + @Mock private IRI mockObjectIRI; + @Mock private IRI mockGraphIRI; + @Mock private BNode mockSubjectBNode; + @Mock private BNode mockObjectBNode; + @Mock private BNode mockGraphBNode; + @Mock private Literal mockSimpleLiteral; + @Mock private Literal mockLangLiteral; + @Mock private Literal mockTypedLiteral; + @Mock private IRI mockDatatypeIRI; + + + @BeforeEach + void setUp() { + parser = new NQuadsParser(mockModel, mockValueFactory); + + lenient().when(mockValueFactory.createIRI(anyString())).thenAnswer(invocation -> { + String uri = invocation.getArgument(0); + if (uri.equals("http://example.org/subject")) return mockSubjectIRI; + if (uri.equals("http://example.org/predicate")) return mockPredicateIRI; + if (uri.equals("http://example.org/object")) return mockObjectIRI; + if (uri.equals("http://example.org/graph")) return mockGraphIRI; + if (uri.equals("http://www.w3.org/2001/XMLSchema#integer")) return mockDatatypeIRI; + return mock(IRI.class); + }); + + lenient().when(mockValueFactory.createBNode(anyString())).thenAnswer(invocation -> { + String label = invocation.getArgument(0); + if (label.equals("sub1")) return mockSubjectBNode; + if (label.equals("obj1")) return mockObjectBNode; + if (label.equals("graph1")) return mockGraphBNode; + return mock(BNode.class); + }); + + lenient().when(mockValueFactory.createLiteral(eq("simple string"))).thenReturn(mockSimpleLiteral); + lenient().when(mockValueFactory.createLiteral(eq("hello"), eq("en"))).thenReturn(mockLangLiteral); + lenient().when(mockValueFactory.createLiteral(eq("123"), any(IRI.class))).thenReturn(mockTypedLiteral); + } + + @Test + @DisplayName("Test get RDF format returns NQUADS") + void testGetRDFFormat() { + assertEquals(RdfFormat.NQUADS, parser.getRDFFormat()); + } + + @Test + @DisplayName("Test parsing a basic quad with IRI graph") + void testParseBasicQuadWithIRIGraph() throws ParsingErrorException { + String nquad = " ."; + parser.parse(new StringReader(nquad)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI, mockGraphIRI); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("Test parsing a basic quad with BNode graph") + void testParseBasicQuadWithBNodeGraph() throws ParsingErrorException { + String nquad = " _:graph1 ."; + parser.parse(new StringReader(nquad)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI, mockGraphBNode); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("Test parsing a quad with a literal object and IRI graph") + void testParseQuadWithLiteralObjectAndIRIGraph() throws ParsingErrorException { + String nquad = " \"simple string\" ."; + parser.parse(new StringReader(nquad)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockSimpleLiteral, mockGraphIRI); + } + + @Test + @DisplayName("Test parsing a triple (no graph) which should go to default graph") + void testParseTripleToDefaultGraph() throws ParsingErrorException { + String nquad = " ."; + parser.parse(new StringReader(nquad)); + + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("Test that invalid N-Quads (less than 3 parts) throws ParsingErrorException") + void testInvalidNQuadsThrowsParsingErrorException_TooFewParts() { + String nquad = " ."; + StringReader reader = new StringReader(nquad); + assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); + } + + @Test + @DisplayName("Test that invalid N-Quads (missing period) is warned and skipped") + void testInvalidNQuadsMissingPeriodIsSkipped() throws ParsingErrorException { + String nquad = " "; + parser.parse(new StringReader(nquad)); + verifyNoInteractions(mockModel); + } + + + @Test + @DisplayName("Test that invalid IRI format throws ParsingErrorException") + void testParseInvalidResourceThrowsException() { + String nquad = "invalid_subject ."; + Reader reader = new StringReader(nquad); + assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); + } + + @Test + @DisplayName("Test that invalid literal format throws ParsingErrorException") + void testParseInvalidLiteralThrowsException() { + String nquad = " \"unclosed literal ."; + Reader reader = new StringReader(nquad); + assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); + } + +} \ No newline at end of file diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParserTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParserTest.java new file mode 100644 index 000000000..aaba86948 --- /dev/null +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParserTest.java @@ -0,0 +1,258 @@ +package fr.inria.corese.core.next.impl.io.parser.ntriples; + +import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.base.io.RdfFormat; +import fr.inria.corese.core.next.impl.exception.ParsingErrorException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; // Import DisplayName +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.Reader; +import java.io.StringReader; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +class NTriplesParserTest { + + @Mock + private Model mockModel; + + @Mock + private ValueFactory mockValueFactory; + + private NTriplesParser parser; + + @Mock + private IRI mockSubjectIRI; + @Mock + private IRI mockPredicateIRI; + @Mock + private IRI mockObjectIRI; + @Mock + private BNode mockSubjectBNode; + @Mock + private BNode mockObjectBNode; + @Mock + private Literal mockSimpleLiteral; + @Mock + private Literal mockLangLiteral; + @Mock + private Literal mockTypedLiteral; + @Mock + private IRI mockDatatypeIRI; + + + @BeforeEach + void setUp() { + parser = new NTriplesParser(mockModel, mockValueFactory); + + + lenient().when(mockValueFactory.createIRI(anyString())).thenAnswer(invocation -> { + String uri = invocation.getArgument(0); + + if (uri.equals("http://example.org/subject")) return mockSubjectIRI; + if (uri.equals("http://example.org/predicate")) return mockPredicateIRI; + if (uri.equals("http://example.org/object")) return mockObjectIRI; + if (uri.equals("http://www.w3.org/2001/XMLSchema#integer")) return mockDatatypeIRI; + return mock(IRI.class); + }); + + lenient().when(mockValueFactory.createBNode(anyString())).thenAnswer(invocation -> { + String label = invocation.getArgument(0); + if (label.equals("sub1")) return mockSubjectBNode; + if (label.equals("obj1")) return mockObjectBNode; + return mock(BNode.class); + }); + + lenient().when(mockValueFactory.createLiteral(eq("simple string"))).thenReturn(mockSimpleLiteral); + lenient().when(mockValueFactory.createLiteral(eq("hello"), eq("en"))).thenReturn(mockLangLiteral); + lenient().when(mockValueFactory.createLiteral(eq("123"), any(IRI.class))).thenReturn(mockTypedLiteral); + } + + @Test + @DisplayName("Test get RDF format returns NTRIPLES") + void testGetRDFFormat() { + assertEquals(RdfFormat.NTRIPLES, parser.getRDFFormat()); + } + + @Test + @DisplayName("Test parsing a basic triple") + void testParseBasicTriple() throws ParsingErrorException { + String ntriple = " ."; + parser.parse(new StringReader(ntriple)); + + verify(mockValueFactory).createIRI("http://example.org/subject"); + verify(mockValueFactory).createIRI("http://example.org/predicate"); + verify(mockValueFactory).createIRI("http://example.org/object"); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("Test parsing a triple with a blank node subject") + void testParseBlankNodeSubject() throws ParsingErrorException { + String ntriple = "_:sub1 ."; + parser.parse(new StringReader(ntriple)); + + verify(mockValueFactory).createBNode("sub1"); + verify(mockValueFactory).createIRI("http://example.org/predicate"); + verify(mockValueFactory).createIRI("http://example.org/object"); + + verify(mockModel).add(mockSubjectBNode, mockPredicateIRI, mockObjectIRI); + } + + @Test + @DisplayName("Test parsing a triple with a blank node object") + void testParseBlankNodeObject() throws ParsingErrorException { + String ntriple = " _:obj1 ."; + parser.parse(new StringReader(ntriple)); + + verify(mockValueFactory).createIRI("http://example.org/subject"); + verify(mockValueFactory).createIRI("http://example.org/predicate"); + verify(mockValueFactory).createBNode("obj1"); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectBNode); + } + + @Test + @DisplayName("Test parsing a simple literal") + void testParseSimpleLiteral() throws ParsingErrorException { + String ntriple = " \"simple string\" ."; + parser.parse(new StringReader(ntriple)); + + verify(mockValueFactory).createLiteral("simple string"); + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockSimpleLiteral); + } + + @Test + @DisplayName("Test parsing a language-tagged literal") + void testParseLanguageTaggedLiteral() throws ParsingErrorException { + String ntriple = " \"hello\"@en ."; + parser.parse(new StringReader(ntriple)); + + verify(mockValueFactory).createLiteral("hello", "en"); + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockLangLiteral); + } + + @Test + @DisplayName("Test parsing a typed literal") + void testParseTypedLiteral() throws ParsingErrorException { + String ntriple = " \"123\"^^ ."; + parser.parse(new StringReader(ntriple)); + + ArgumentCaptor datatypeCaptor = ArgumentCaptor.forClass(IRI.class); + verify(mockValueFactory).createLiteral(eq("123"), datatypeCaptor.capture()); + verify(mockValueFactory).createIRI("http://www.w3.org/2001/XMLSchema#integer"); + + assertEquals(mockDatatypeIRI, datatypeCaptor.getValue()); + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockTypedLiteral); + } + + @Test + @DisplayName("Test parsing multiple triples in one go") + void testParseMultipleTriples() throws ParsingErrorException { + String ntriples = + " .\n" + + "_:b2 \"literal\"@fr ."; + parser.parse(new StringReader(ntriples)); + + + verify(mockValueFactory).createIRI("http://example.org/s1"); + verify(mockValueFactory).createIRI("http://example.org/p1"); + verify(mockValueFactory).createIRI("http://example.org/o1"); + verify(mockModel).add(any(IRI.class), any(IRI.class), any(IRI.class)); + + + verify(mockValueFactory).createBNode("b2"); + verify(mockValueFactory).createIRI("http://example.org/p2"); + verify(mockValueFactory).createLiteral("literal", "fr"); + + ArgumentCaptor subjCaptor = ArgumentCaptor.forClass(Resource.class); + ArgumentCaptor predCaptor = ArgumentCaptor.forClass(IRI.class); + ArgumentCaptor objCaptor = ArgumentCaptor.forClass(Value.class); + verify(mockModel, times(2)).add(subjCaptor.capture(), predCaptor.capture(), objCaptor.capture()); + } + + @Test + @DisplayName("Test parsing with comments and empty lines ignored") + void testParseWithCommentsAndEmptyLines() throws ParsingErrorException { + String ntriples = """ + # This is a comment + + . + # Another comment with leading spaces + + """; + + + parser.parse(new StringReader(ntriples)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI); + verifyNoMoreInteractions(mockModel); + } + + + @ParameterizedTest + @ValueSource(strings = { + " .", + " " + }) + @DisplayName("Invalid N-Triples should throw ParsingErrorException") + void testInvalidNTriplesThrowsParsingErrorException(String invalidNTriple) { + StringReader reader = new StringReader(invalidNTriple); + assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); + } + + @Test + @DisplayName("Test that invalid line (too few parts) throws ParsingErrorException") + void testParseInvalidLineTooFewPartsThrowsException() { + String ntriple = " ."; + StringReader reader = new StringReader(ntriple); + + assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); + } + + @Test + @DisplayName("Test that invalid IRI format throws ParsingErrorException") + void testParseInvalidResourceThrowsException() { + String ntriple = "invalid_subject ."; + Reader reader = new StringReader(ntriple); + + assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); + } + + @Test + @DisplayName("Test that invalid predicate IRI format throws ParsingErrorException") + void testParseInvalidIRITThrowsException() { + String ntriple = " invalid_predicate ."; + StringReader reader = new StringReader(ntriple); + + assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); + } + @Test + @DisplayName("Test that invalid literal format throws ParsingErrorException") + void testParseInvalidLiteralThrowsException() { + String ntriple = " \"unclosed literal ."; + Reader reader = new StringReader(ntriple); + + assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); + } + + @Test + @DisplayName("Test that I/O errors during parsing throw ParsingErrorException") + void testParseIOErrorThrowsException() { + StringReader reader = new StringReader("test line ."); + reader.close(); + + assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); + } +} From 3e85eac9a0cc8e1083162a53fd5c6872f1cb83e3 Mon Sep 17 00:00:00 2001 From: "AD\\aabdoun" Date: Wed, 9 Jul 2025 10:05:00 +0200 Subject: [PATCH 02/15] =?UTF-8?q?l=E2=80=99issue=2088=20parseur=20NTriples?= =?UTF-8?q?=20ANTLRNTriplesParser?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/antlr/NQuads.g4 | 111 ++++++++ src/main/antlr/NTriples.g4 | 113 ++++++++ .../next/impl/io/parser/ParserFactory.java | 13 +- .../impl/io/parser/nquads/NQuadsParser.java | 259 ------------------ .../parser/ntriples/ANTLRNTriplesParser.java | 4 + .../io/parser/ntriples/NTriplesParser.java | 179 ------------ .../datatype/function/StringHelper.java | 1 - .../io/parser/nquads/NQuadsParserTest.java | 151 ---------- .../parser/ntriples/NTriplesParserTest.java | 258 ----------------- 9 files changed, 237 insertions(+), 852 deletions(-) create mode 100644 src/main/antlr/NQuads.g4 create mode 100644 src/main/antlr/NTriples.g4 delete mode 100644 src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParser.java create mode 100644 src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java delete mode 100644 src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParser.java delete mode 100644 src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParserTest.java delete mode 100644 src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParserTest.java diff --git a/src/main/antlr/NQuads.g4 b/src/main/antlr/NQuads.g4 new file mode 100644 index 000000000..ad04f65ff --- /dev/null +++ b/src/main/antlr/NQuads.g4 @@ -0,0 +1,111 @@ +grammar NQuads; + +nquadsDoc + : statement? (EOL* statement)* EOL* + ; + +statement + : subject predicate object graphLabel? '.' + ; + +subject + : IRIREF + | BLANK_NODE_LABEL + ; + +predicate + : IRIREF + ; + +object + : IRIREF + | BLANK_NODE_LABEL + | literal + ; + +graphLabel + : IRIREF + | BLANK_NODE_LABEL + ; + +literal + : STRING_LITERAL_QUOTE ('^^' IRIREF | LANGTAG)? + ; + +LANGTAG + : '@' [a-zA-Z]+ ('-' [a-zA-Z0-9]+)* + ; + +EOL + : [\u000D\u000A]+ + ; + +IRIREF +// '<' ([^#x00-#x20<>"{}|^`\] | UCHAR)* '>' + : '<' [a-zA-Z0-9-]+':' ((~( [\u0000-\u0020] | '<' | '>' | '"' | '{'| '}' | '|'| '^'| '`' | '\\' )) | UCHAR)* '>' + ; + +STRING_LITERAL_QUOTE + : '"' ( ~( [\u0022] | [\u005C] | [\u000A] | [\u000D] ) | ECHAR | UCHAR )* '"' + ; + +BLANK_NODE_LABEL +// '_:' (PN_CHARS_U | [0-9]) ((PN_CHARS | '.')* PN_CHARS)? + : '_:' (PN_CHARS_U | [0-9]) ((PN_CHARS | '.')* PN_CHARS)? + ; + +UCHAR + : '\\u' HEX HEX HEX HEX + | '\\U' HEX HEX HEX HEX HEX HEX HEX HEX + ; + +HEX + : [0-9] + | [A-F] + | [a-f] + ; + +ECHAR + : '\\' [tbnrf"'\\] + ; + +PN_CHARS_BASE + : 'A' .. 'Z' + | 'a' .. 'z' + | '\u00C0' .. '\u00D6' + | '\u00D8' .. '\u00F6' + | '\u00F8' .. '\u02FF' + | '\u0370' .. '\u037D' + | '\u037F' .. '\u1FFF' + | '\u200C' .. '\u200D' + | '\u2070' .. '\u218F' + | '\u2C00' .. '\u2FEF' + | '\u3001' .. '\uD7FF' + | '\uF900' .. '\uFDCF' + | '\uFDF0' .. '\uFFFD' +// | '\u10000' .. '\uEFFFF' + ; + +PN_CHARS_U +// PN_CHARS_BASE | '_' | ':' + : PN_CHARS_BASE + | '_' +// | ':' + ; + +PN_CHARS + : PN_CHARS_U + | '-' + | [0-9] + | [\u00B7] + | [\u0300-\u036F] + | [\u203F-\u2040] + ; + +LC + : '#' ~[\r\n]* -> channel(HIDDEN) + ; + +WS + : ([\t\r\n\u000C] | ' ')+ -> skip + ; \ No newline at end of file diff --git a/src/main/antlr/NTriples.g4 b/src/main/antlr/NTriples.g4 new file mode 100644 index 000000000..783d53ce0 --- /dev/null +++ b/src/main/antlr/NTriples.g4 @@ -0,0 +1,113 @@ + +// $antlr-format alignTrailingComments true, columnLimit 150, minEmptyLines 1, maxEmptyLinesToKeep 1, reflowComments false, useTab false +// $antlr-format allowShortRulesOnASingleLine false, allowShortBlocksOnASingleLine true, alignSemicolons hanging, alignColons hanging + + +grammar NTriples; + + +ntriplesDoc +// triple? (EOL triple)* EOL? + : triple? (EOL* triple)* EOL* + ; + +triple + : subject predicate object '.' + ; + +subject + : IRIREF + | BLANK_NODE_LABEL + ; + +predicate + : IRIREF + ; + +object + : IRIREF + | BLANK_NODE_LABEL + | literal + ; + +literal + : STRING_LITERAL_QUOTE ('^^' IRIREF | LANGTAG)? + ; + +LANGTAG + : '@' [a-zA-Z]+ ('-' [a-zA-Z0-9]+)* + ; + +EOL + : [\u000D\u000A]+ + ; + +IRIREF +// '<' ([^#x00-#x20<>"{}|^`\] | UCHAR)* '>' + : '<' [a-zA-Z0-9-]+':' ((~( [\u0000-\u0020] | '<' | '>' | '"' | '{'| '}' | '|'| '^'| '`' | '\\' )) | UCHAR)* '>' + ; + +STRING_LITERAL_QUOTE + : '"' ( ~( [\u0022] | [\u005C] | [\u000A] | [\u000D] ) | ECHAR | UCHAR )* '"' + ; + +BLANK_NODE_LABEL +// '_:' (PN_CHARS_U | [0-9]) ((PN_CHARS | '.')* PN_CHARS)? + : '_:' (PN_CHARS_U | [0-9]) ((PN_CHARS | '.')* PN_CHARS)? + ; + +UCHAR + : '\\u' HEX HEX HEX HEX + | '\\U' HEX HEX HEX HEX HEX HEX HEX HEX + ; + +HEX + : [0-9] + | [A-F] + | [a-f] + ; + +ECHAR + : '\\' [tbnrf"'\\] + ; + +PN_CHARS_BASE + : 'A' .. 'Z' + | 'a' .. 'z' + | '\u00C0' .. '\u00D6' + | '\u00D8' .. '\u00F6' + | '\u00F8' .. '\u02FF' + | '\u0370' .. '\u037D' + | '\u037F' .. '\u1FFF' + | '\u200C' .. '\u200D' + | '\u2070' .. '\u218F' + | '\u2C00' .. '\u2FEF' + | '\u3001' .. '\uD7FF' + | '\uF900' .. '\uFDCF' + | '\uFDF0' .. '\uFFFD' +// | '\u10000' .. '\uEFFFF' + ; + +PN_CHARS_U +// PN_CHARS_BASE | '_' | ':' + : PN_CHARS_BASE + | '_' +// | ':' + ; + +PN_CHARS + : PN_CHARS_U + | '-' + | [0-9] + | [\u00B7] + | [\u0300-\u036F] + | [\u203F-\u2040] + ; + +LC + : '#' ~[\r\n]* -> channel(HIDDEN) + ; + +WS + : ([\t\r\n\u000C] | ' ')+ -> skip + ; \ No newline at end of file diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ParserFactory.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ParserFactory.java index a331284b5..1e0cfdf06 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ParserFactory.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ParserFactory.java @@ -7,6 +7,7 @@ import fr.inria.corese.core.next.api.io.parser.RDFParser; import fr.inria.corese.core.next.api.io.parser.RDFParserOptions; import fr.inria.corese.core.next.impl.io.parser.jsonld.JSONLDParser; +import fr.inria.corese.core.next.impl.io.parser.ntriples.ANTLRNTriplesParser; import fr.inria.corese.core.next.impl.io.parser.turtle.ANTLRTurtleParser; /** @@ -34,10 +35,12 @@ public ParserFactory() { */ @Override public RDFParser createRDFParser(RDFFormat format, Model model, ValueFactory factory, RDFParserOptions config) { - if(format == RDFFormat.JSONLD) { + if (format == RDFFormat.JSONLD) { return new JSONLDParser(model, factory, config); - } else if(format == RDFFormat.TURTLE) { + } else if (format == RDFFormat.TURTLE) { return new ANTLRTurtleParser(model, factory, config); + } else if (format == RdfFormat.NTRIPLES) { + return new ANTLRNTriplesParser(model, factory, config); } throw new IllegalArgumentException("Unsupported format: " + format); } @@ -51,10 +54,12 @@ public RDFParser createRDFParser(RDFFormat format, Model model, ValueFactory fac */ @Override public RDFParser createRDFParser(RDFFormat format, Model model, ValueFactory factory) { - if(format == RDFFormat.JSONLD) { + if (format == RDFFormat.JSONLD) { return new JSONLDParser(model, factory); - } else if(format == RDFFormat.TURTLE) { + } else if (format == RDFFormat.TURTLE) { return new ANTLRTurtleParser(model, factory); + } else if (format == RdfFormat.NTRIPLES) { + return new ANTLRNTriplesParser(model, factory); } throw new IllegalArgumentException("Unsupported format: " + format); } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParser.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParser.java deleted file mode 100644 index 5f2d95184..000000000 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParser.java +++ /dev/null @@ -1,259 +0,0 @@ -package fr.inria.corese.core.next.impl.io.parser.nquads; - -import fr.inria.corese.core.next.api.*; -import fr.inria.corese.core.next.api.base.io.RdfFormat; -import fr.inria.corese.core.next.api.base.io.parser.AbstractParser; -import fr.inria.corese.core.next.api.io.IOConfig; -import fr.inria.corese.core.next.impl.exception.ParsingErrorException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - -/** - * A simplified parser for N-Quads format. - * This parser reads N-Quads line by line and attempts to parse - * subject, predicate, object, and an optional graph, then adds them to the model. - * - * IMPORTANT LIMITATION: This is a basic implementation. The `splitNqLine` method - * is simplistic and WILL NOT correctly handle complex RDF terms, especially literals - * containing spaces or intricate escaping, when trying to differentiate the object from the graph. - * For production use, a more robust N-Quads parsing library would be highly recommended, - * ideally one that uses a proper state machine or lexer. - */ -public class NQuadsParser extends AbstractParser { - - private static final Logger logger = LoggerFactory.getLogger(NQuadsParser.class); - - public NQuadsParser(Model model, ValueFactory factory) { - super(model, factory); - } - - public NQuadsParser(Model model, ValueFactory factory, IOConfig config) { - super(model, factory); - setConfig(config); - } - - @Override - public RdfFormat getRDFFormat() { - return RdfFormat.NQUADS; - } - - @Override - public void parse(InputStream in, String baseURI) throws ParsingErrorException { - parse(new InputStreamReader(in, StandardCharsets.UTF_8), baseURI); - } - - @Override - public void parse(Reader reader, String baseURI) throws ParsingErrorException { - try (BufferedReader bufferedReader = new BufferedReader(reader)) { - String line; - int lineNumber = 0; - while ((line = bufferedReader.readLine()) != null) { - lineNumber++; - line = line.trim(); - if (line.isEmpty() || line.startsWith("#")) { - continue; - } - - - if (!line.endsWith(".")) { - logger.warn("Line {} does not end with a period and will be skipped: {}", lineNumber, line); - - continue; - } - - - line = line.substring(0, line.length() - 1).trim(); - - processLine(line, lineNumber); - } - } catch (Exception e) { - if (e instanceof ParsingErrorException) { - throw (ParsingErrorException) e; - } - throw new ParsingErrorException("Error reading N-Quads input: " + e.getMessage(), e); - } - } - - /** - * Processes a single N-Quads line, parsing its components and adding them to the model. - * This method attempts to differentiate between N-Triples (default graph) and N-Quads (named graph). - * - * @param line The N-Quads line to process. - * @param lineNumber The line number for error reporting. - * @throws ParsingErrorException if the line cannot be parsed according to N-Quads syntax. - */ - private void processLine(String line, int lineNumber) throws ParsingErrorException { - try { - String[] parts = splitNqLine(line); - - if (parts.length < 3) { - logger.error("Invalid N-Quads line (less than 3 parts) at line {}: {}", lineNumber, line); - throw new ParsingErrorException("Invalid N-Quads line at line " + lineNumber + ": " + line); - } - if (parts.length > 4) { - logger.warn("Line {} has more than 4 parts after initial split. This likely indicates a literal with spaces that was not handled correctly by the simple parser: {}", lineNumber, line); - throw new ParsingErrorException("Invalid N-Quads line (too many parts, likely literal parsing issue) at line " + lineNumber + ": " + line); - } - - Resource subject = parseResource(parts[0]); - IRI predicate = parseIRI(parts[1]); - Value object = parseValue(parts[2]); - - Resource graph = null; - if (parts.length == 4) { - graph = parseResource(parts[3]); - } - - - if (graph != null) { - getModel().add(subject, predicate, object, graph); - } else { - - getModel().add(subject, predicate, object); - } - - } catch (IllegalArgumentException e) { - throw new ParsingErrorException("Error parsing N-Quads line " + lineNumber + ": " + line, e); - } catch (Exception e) { - throw new ParsingErrorException("Unexpected error processing N-Quads line " + lineNumber + ": " + line, e); - } - } - - /** - * A very simplistic split for N-Quads. It tries to identify the main components. - * This will fail for complex literals containing spaces or quoted parts within, - * especially when a graph IRI is also present. - * A more robust parser would use a proper state machine or regex for parsing RDF terms. - * It attempts to differentiate between a 3-part triple (default graph) and a 4-part quad (named graph). - */ - private String[] splitNqLine(String line) { - - - List parts = new ArrayList<>(); - int currentPos = 0; - - for (int i = 0; i < 4; i++) { - line = line.substring(currentPos).trim(); - if (line.isEmpty()) break; - - String term; - if (line.startsWith("<")) { - int endIndex = line.indexOf('>'); - if (endIndex == -1) throw new IllegalArgumentException("Unclosed IRI: " + line); - term = line.substring(0, endIndex + 1); - currentPos = term.length(); - } else if (line.startsWith("_:")) { - int endIndex = line.indexOf(' '); - if (endIndex == -1) endIndex = line.length(); - term = line.substring(0, endIndex); - currentPos = term.length(); - } else if (line.startsWith("\"")) { - int firstQuote = line.indexOf('"'); - int lastQuote = -1; - - boolean inEscape = false; - for (int j = firstQuote + 1; j < line.length(); j++) { - char c = line.charAt(j); - if (c == '\\' && !inEscape) { - inEscape = true; - } else if (c == '"' && !inEscape) { - lastQuote = j; - break; - } else { - inEscape = false; - } - } - - if (lastQuote == -1) throw new IllegalArgumentException("Unclosed literal: " + line); - term = line.substring(0, lastQuote + 1); - currentPos = term.length(); - - - String remainder = line.substring(lastQuote + 1).trim(); - if (remainder.startsWith("^^<")) { - int dtEndIndex = remainder.indexOf('>'); - if (dtEndIndex == -1) throw new IllegalArgumentException("Unclosed datatype URI: " + remainder); - term += remainder.substring(0, dtEndIndex + 1); - currentPos += (dtEndIndex + 1) + (remainder.length() - remainder.trim().length()); - } else if (remainder.startsWith("@")) { - int langEndIndex = remainder.indexOf(' '); - if (langEndIndex == -1) langEndIndex = remainder.length(); - term += remainder.substring(0, langEndIndex); - currentPos += (langEndIndex) + (remainder.length() - remainder.trim().length()); - } - } else { - - throw new IllegalArgumentException("Malformed RDF term: " + line); - } - - parts.add(term.trim()); - line = line.substring(currentPos); - currentPos = 0; - } - - return parts.toArray(new String[0]); - } - - - /** - * Parses an N-Triples/N-Quads resource part (IRI or Blank Node). - */ - private Resource parseResource(String part) { - if (part.startsWith("<") && part.endsWith(">")) { - return getValueFactory().createIRI(part.substring(1, part.length() - 1)); - } else if (part.startsWith("_:")) { - return getValueFactory().createBNode(part.substring(2)); - } - throw new IllegalArgumentException("Invalid N-Quads resource: " + part); - } - - /** - * Parses an N-Triples/N-Quads IRI part. - */ - private IRI parseIRI(String part) { - if (part.startsWith("<") && part.endsWith(">")) { - return getValueFactory().createIRI(part.substring(1, part.length() - 1)); - } - throw new IllegalArgumentException("Invalid N-Quads IRI: " + part); - } - - /** - * Parses an N-Triples/N-Quads value part (IRI, Blank Node, or Literal). - * This is a highly simplified literal parser and does not handle complex escaping well. - */ - private Value parseValue(String part) { - if (part.startsWith("<") && part.endsWith(">")) { - return getValueFactory().createIRI(part.substring(1, part.length() - 1)); - } else if (part.startsWith("_:")) { - return getValueFactory().createBNode(part.substring(2)); - } else if (part.startsWith("\"")) { - int lastQuote = part.lastIndexOf('"'); - if (lastQuote == -1 || lastQuote == 0) { - throw new IllegalArgumentException("Invalid N-Quads literal: " + part); - } - String literalValue = part.substring(1, lastQuote); - - String remainder = part.substring(lastQuote + 1); - - if (remainder.startsWith("^^<") && remainder.endsWith(">")) { - String datatypeUri = remainder.substring(3, remainder.length() - 1); - return getValueFactory().createLiteral(literalValue, getValueFactory().createIRI(datatypeUri)); - } else if (remainder.startsWith("@")) { - String lang = remainder.substring(1); - return getValueFactory().createLiteral(literalValue, lang); - } else if (remainder.isEmpty()) { - return getValueFactory().createLiteral(literalValue); - } - throw new IllegalArgumentException("Invalid N-Quads literal format: " + part); - } - throw new IllegalArgumentException("Invalid N-Quads value: " + part); - } -} \ No newline at end of file diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java new file mode 100644 index 000000000..291d67d9f --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java @@ -0,0 +1,4 @@ +package fr.inria.corese.core.next.impl.io.parser.ntriples; + +public class ANTLRNTriplesParser { +} diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParser.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParser.java deleted file mode 100644 index 55f020cd1..000000000 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParser.java +++ /dev/null @@ -1,179 +0,0 @@ -package fr.inria.corese.core.next.impl.io.parser.ntriples; - -import fr.inria.corese.core.next.api.*; -import fr.inria.corese.core.next.api.base.io.RdfFormat; -import fr.inria.corese.core.next.api.base.io.parser.AbstractParser; -import fr.inria.corese.core.next.api.io.IOConfig; -import fr.inria.corese.core.next.impl.exception.ParsingErrorException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.nio.charset.StandardCharsets; - -/** - * A simplified parser for N-Triples format. - * This parser reads N-Triples line by line and attempts to parse - * subject, predicate, and object, then adds them to the model. - * It's a basic implementation and might not handle all edge cases or - * complex RDF term structures (like intricate literal escaping) perfectly. - * For production use, a more robust N-Triples parsing library would be recommended. - */ -public class NTriplesParser extends AbstractParser { - - private static final Logger logger = LoggerFactory.getLogger(NTriplesParser.class); - - public NTriplesParser(Model model, ValueFactory factory) { - super(model, factory); - } - - public NTriplesParser(Model model, ValueFactory factory, IOConfig config) { - super(model, factory); - setConfig(config); - } - - @Override - public RdfFormat getRDFFormat() { - return RdfFormat.NTRIPLES; - } - - @Override - public void parse(InputStream in, String baseURI) throws ParsingErrorException { - parse(new InputStreamReader(in, StandardCharsets.UTF_8), baseURI); - } - - @Override - public void parse(Reader reader, String baseURI) throws ParsingErrorException { - try (BufferedReader bufferedReader = new BufferedReader(reader)) { - String line; - int lineNumber = 0; - while ((line = bufferedReader.readLine()) != null) { - lineNumber++; - line = line.trim(); - if (line.isEmpty() || line.startsWith("#")) { - continue; - } - - if (!line.endsWith(".")) { - logger.warn("Line {} does not end with a period: {}", lineNumber, line); - } - - line = line.substring(0, line.length() - 1).trim(); - - processLine(line, lineNumber); - - } - } catch (Exception e) { - - if (e instanceof ParsingErrorException) { - throw (ParsingErrorException) e; - } - throw new ParsingErrorException("Error reading N-Triples input: " + e.getMessage(), e); - } - } - - /** - * Processes a single N-Triples line, parsing its components and adding them to the model. - * - * @param line The N-Triples line to process. - * @param lineNumber The line number for error reporting. - * @throws ParsingErrorException if the line cannot be parsed according to N-Triples syntax. - */ - private void processLine(String line, int lineNumber) throws ParsingErrorException { - try { - String[] parts = splitNtLine(line); - - if (parts.length < 3) { - logger.error("Invalid N-Triples line (less than 3 parts) at line {}: {}", lineNumber, line); - throw new ParsingErrorException("Invalid N-Triples line at line " + lineNumber + ": " + line); - } - - Resource subject = parseResource(parts[0]); - IRI predicate = parseIRI(parts[1]); - Value object = parseValue(parts[2]); - - getModel().add(subject, predicate, object); - - } catch (IllegalArgumentException e) { - throw new ParsingErrorException("Error parsing N-Triples line " + lineNumber + ": " + line, e); - } catch (Exception e) { - throw new ParsingErrorException("Unexpected error processing N-Triples line " + lineNumber + ": " + line, e); - } - } - - /** - * A very simplistic split for N-Triples. It tries to identify the main components. - * This will fail for complex literals containing spaces or quoted parts within. - * A more robust parser would use a proper state machine or regex for parsing RDF terms. - */ - private String[] splitNtLine(String line) { - int firstSpace = line.indexOf(' '); - if (firstSpace == -1) return new String[0]; - String subjectPart = line.substring(0, firstSpace).trim(); - String remaining = line.substring(firstSpace).trim(); - - int secondSpace = remaining.indexOf(' '); - if (secondSpace == -1) return new String[0]; - String predicatePart = remaining.substring(0, secondSpace).trim(); - String objectPart = remaining.substring(secondSpace).trim(); - - return new String[]{subjectPart, predicatePart, objectPart}; - } - - /** - * Parses an N-Triples resource part (IRI or Blank Node). - */ - private Resource parseResource(String part) { - if (part.startsWith("<") && part.endsWith(">")) { - return getValueFactory().createIRI(part.substring(1, part.length() - 1)); - } else if (part.startsWith("_:")) { - return getValueFactory().createBNode(part.substring(2)); - } - throw new IllegalArgumentException("Invalid N-Triples resource: " + part); - } - - /** - * Parses an N-Triples IRI part. - */ - private IRI parseIRI(String part) { - if (part.startsWith("<") && part.endsWith(">")) { - return getValueFactory().createIRI(part.substring(1, part.length() - 1)); - } - throw new IllegalArgumentException("Invalid N-Triples IRI: " + part); - } - - /** - * Parses an N-Triples value part (IRI, Blank Node, or Literal). - * This is a highly simplified literal parser. - */ - private Value parseValue(String part) { - if (part.startsWith("<") && part.endsWith(">")) { - return getValueFactory().createIRI(part.substring(1, part.length() - 1)); - } else if (part.startsWith("_:")) { - return getValueFactory().createBNode(part.substring(2)); - } else if (part.startsWith("\"")) { - int lastQuote = part.lastIndexOf('"'); - if (lastQuote == -1 || lastQuote == 0) { - throw new IllegalArgumentException("Invalid N-Triples literal: " + part); - } - String literalValue = part.substring(1, lastQuote); - - String remainder = part.substring(lastQuote + 1); - - if (remainder.startsWith("^^<") && remainder.endsWith(">")) { - String datatypeUri = remainder.substring(3, remainder.length() - 1); - return getValueFactory().createLiteral(literalValue, getValueFactory().createIRI(datatypeUri)); - } else if (remainder.startsWith("@")) { - String lang = remainder.substring(1); - return getValueFactory().createLiteral(literalValue, lang); - } else if (remainder.isEmpty()) { - return getValueFactory().createLiteral(literalValue); - } - throw new IllegalArgumentException("Invalid N-Triples literal format: " + part); - } - throw new IllegalArgumentException("Invalid N-Triples value: " + part); - } -} diff --git a/src/main/java/fr/inria/corese/core/sparql/datatype/function/StringHelper.java b/src/main/java/fr/inria/corese/core/sparql/datatype/function/StringHelper.java index 87581a0bc..30d6bbb25 100644 --- a/src/main/java/fr/inria/corese/core/sparql/datatype/function/StringHelper.java +++ b/src/main/java/fr/inria/corese/core/sparql/datatype/function/StringHelper.java @@ -358,7 +358,6 @@ public static int indexOfWordIgnoreCaseAccentAndPlurial(String string1, String s * qualifier, scientific notation and numbers marked with a type * qualifier (e.g. 123L). * - * @see org.apache.commons.lang3.math.NumberUtils * @param str the string to check * @return true if the string denotes a correctly formatted number, false otherwise. */ diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParserTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParserTest.java deleted file mode 100644 index b793184c3..000000000 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsParserTest.java +++ /dev/null @@ -1,151 +0,0 @@ -package fr.inria.corese.core.next.impl.io.parser.nquads; - - -import fr.inria.corese.core.next.api.*; -import fr.inria.corese.core.next.api.base.io.RdfFormat; -import fr.inria.corese.core.next.impl.exception.ParsingErrorException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.io.Reader; -import java.io.StringReader; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -@ExtendWith(MockitoExtension.class) -class NQuadsParserTest { - - @Mock - private Model mockModel; - - @Mock - private ValueFactory mockValueFactory; - - private NQuadsParser parser; - - - @Mock private IRI mockSubjectIRI; - @Mock private IRI mockPredicateIRI; - @Mock private IRI mockObjectIRI; - @Mock private IRI mockGraphIRI; - @Mock private BNode mockSubjectBNode; - @Mock private BNode mockObjectBNode; - @Mock private BNode mockGraphBNode; - @Mock private Literal mockSimpleLiteral; - @Mock private Literal mockLangLiteral; - @Mock private Literal mockTypedLiteral; - @Mock private IRI mockDatatypeIRI; - - - @BeforeEach - void setUp() { - parser = new NQuadsParser(mockModel, mockValueFactory); - - lenient().when(mockValueFactory.createIRI(anyString())).thenAnswer(invocation -> { - String uri = invocation.getArgument(0); - if (uri.equals("http://example.org/subject")) return mockSubjectIRI; - if (uri.equals("http://example.org/predicate")) return mockPredicateIRI; - if (uri.equals("http://example.org/object")) return mockObjectIRI; - if (uri.equals("http://example.org/graph")) return mockGraphIRI; - if (uri.equals("http://www.w3.org/2001/XMLSchema#integer")) return mockDatatypeIRI; - return mock(IRI.class); - }); - - lenient().when(mockValueFactory.createBNode(anyString())).thenAnswer(invocation -> { - String label = invocation.getArgument(0); - if (label.equals("sub1")) return mockSubjectBNode; - if (label.equals("obj1")) return mockObjectBNode; - if (label.equals("graph1")) return mockGraphBNode; - return mock(BNode.class); - }); - - lenient().when(mockValueFactory.createLiteral(eq("simple string"))).thenReturn(mockSimpleLiteral); - lenient().when(mockValueFactory.createLiteral(eq("hello"), eq("en"))).thenReturn(mockLangLiteral); - lenient().when(mockValueFactory.createLiteral(eq("123"), any(IRI.class))).thenReturn(mockTypedLiteral); - } - - @Test - @DisplayName("Test get RDF format returns NQUADS") - void testGetRDFFormat() { - assertEquals(RdfFormat.NQUADS, parser.getRDFFormat()); - } - - @Test - @DisplayName("Test parsing a basic quad with IRI graph") - void testParseBasicQuadWithIRIGraph() throws ParsingErrorException { - String nquad = " ."; - parser.parse(new StringReader(nquad)); - - verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI, mockGraphIRI); - verifyNoMoreInteractions(mockModel); - } - - @Test - @DisplayName("Test parsing a basic quad with BNode graph") - void testParseBasicQuadWithBNodeGraph() throws ParsingErrorException { - String nquad = " _:graph1 ."; - parser.parse(new StringReader(nquad)); - - verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI, mockGraphBNode); - verifyNoMoreInteractions(mockModel); - } - - @Test - @DisplayName("Test parsing a quad with a literal object and IRI graph") - void testParseQuadWithLiteralObjectAndIRIGraph() throws ParsingErrorException { - String nquad = " \"simple string\" ."; - parser.parse(new StringReader(nquad)); - - verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockSimpleLiteral, mockGraphIRI); - } - - @Test - @DisplayName("Test parsing a triple (no graph) which should go to default graph") - void testParseTripleToDefaultGraph() throws ParsingErrorException { - String nquad = " ."; - parser.parse(new StringReader(nquad)); - - - verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI); - verifyNoMoreInteractions(mockModel); - } - - @Test - @DisplayName("Test that invalid N-Quads (less than 3 parts) throws ParsingErrorException") - void testInvalidNQuadsThrowsParsingErrorException_TooFewParts() { - String nquad = " ."; - StringReader reader = new StringReader(nquad); - assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); - } - - @Test - @DisplayName("Test that invalid N-Quads (missing period) is warned and skipped") - void testInvalidNQuadsMissingPeriodIsSkipped() throws ParsingErrorException { - String nquad = " "; - parser.parse(new StringReader(nquad)); - verifyNoInteractions(mockModel); - } - - - @Test - @DisplayName("Test that invalid IRI format throws ParsingErrorException") - void testParseInvalidResourceThrowsException() { - String nquad = "invalid_subject ."; - Reader reader = new StringReader(nquad); - assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); - } - - @Test - @DisplayName("Test that invalid literal format throws ParsingErrorException") - void testParseInvalidLiteralThrowsException() { - String nquad = " \"unclosed literal ."; - Reader reader = new StringReader(nquad); - assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); - } - -} \ No newline at end of file diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParserTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParserTest.java deleted file mode 100644 index aaba86948..000000000 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesParserTest.java +++ /dev/null @@ -1,258 +0,0 @@ -package fr.inria.corese.core.next.impl.io.parser.ntriples; - -import fr.inria.corese.core.next.api.*; -import fr.inria.corese.core.next.api.base.io.RdfFormat; -import fr.inria.corese.core.next.impl.exception.ParsingErrorException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.DisplayName; // Import DisplayName -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.io.Reader; -import java.io.StringReader; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.*; - -@ExtendWith(MockitoExtension.class) -class NTriplesParserTest { - - @Mock - private Model mockModel; - - @Mock - private ValueFactory mockValueFactory; - - private NTriplesParser parser; - - @Mock - private IRI mockSubjectIRI; - @Mock - private IRI mockPredicateIRI; - @Mock - private IRI mockObjectIRI; - @Mock - private BNode mockSubjectBNode; - @Mock - private BNode mockObjectBNode; - @Mock - private Literal mockSimpleLiteral; - @Mock - private Literal mockLangLiteral; - @Mock - private Literal mockTypedLiteral; - @Mock - private IRI mockDatatypeIRI; - - - @BeforeEach - void setUp() { - parser = new NTriplesParser(mockModel, mockValueFactory); - - - lenient().when(mockValueFactory.createIRI(anyString())).thenAnswer(invocation -> { - String uri = invocation.getArgument(0); - - if (uri.equals("http://example.org/subject")) return mockSubjectIRI; - if (uri.equals("http://example.org/predicate")) return mockPredicateIRI; - if (uri.equals("http://example.org/object")) return mockObjectIRI; - if (uri.equals("http://www.w3.org/2001/XMLSchema#integer")) return mockDatatypeIRI; - return mock(IRI.class); - }); - - lenient().when(mockValueFactory.createBNode(anyString())).thenAnswer(invocation -> { - String label = invocation.getArgument(0); - if (label.equals("sub1")) return mockSubjectBNode; - if (label.equals("obj1")) return mockObjectBNode; - return mock(BNode.class); - }); - - lenient().when(mockValueFactory.createLiteral(eq("simple string"))).thenReturn(mockSimpleLiteral); - lenient().when(mockValueFactory.createLiteral(eq("hello"), eq("en"))).thenReturn(mockLangLiteral); - lenient().when(mockValueFactory.createLiteral(eq("123"), any(IRI.class))).thenReturn(mockTypedLiteral); - } - - @Test - @DisplayName("Test get RDF format returns NTRIPLES") - void testGetRDFFormat() { - assertEquals(RdfFormat.NTRIPLES, parser.getRDFFormat()); - } - - @Test - @DisplayName("Test parsing a basic triple") - void testParseBasicTriple() throws ParsingErrorException { - String ntriple = " ."; - parser.parse(new StringReader(ntriple)); - - verify(mockValueFactory).createIRI("http://example.org/subject"); - verify(mockValueFactory).createIRI("http://example.org/predicate"); - verify(mockValueFactory).createIRI("http://example.org/object"); - - verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI); - verifyNoMoreInteractions(mockModel); - } - - @Test - @DisplayName("Test parsing a triple with a blank node subject") - void testParseBlankNodeSubject() throws ParsingErrorException { - String ntriple = "_:sub1 ."; - parser.parse(new StringReader(ntriple)); - - verify(mockValueFactory).createBNode("sub1"); - verify(mockValueFactory).createIRI("http://example.org/predicate"); - verify(mockValueFactory).createIRI("http://example.org/object"); - - verify(mockModel).add(mockSubjectBNode, mockPredicateIRI, mockObjectIRI); - } - - @Test - @DisplayName("Test parsing a triple with a blank node object") - void testParseBlankNodeObject() throws ParsingErrorException { - String ntriple = " _:obj1 ."; - parser.parse(new StringReader(ntriple)); - - verify(mockValueFactory).createIRI("http://example.org/subject"); - verify(mockValueFactory).createIRI("http://example.org/predicate"); - verify(mockValueFactory).createBNode("obj1"); - - verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectBNode); - } - - @Test - @DisplayName("Test parsing a simple literal") - void testParseSimpleLiteral() throws ParsingErrorException { - String ntriple = " \"simple string\" ."; - parser.parse(new StringReader(ntriple)); - - verify(mockValueFactory).createLiteral("simple string"); - verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockSimpleLiteral); - } - - @Test - @DisplayName("Test parsing a language-tagged literal") - void testParseLanguageTaggedLiteral() throws ParsingErrorException { - String ntriple = " \"hello\"@en ."; - parser.parse(new StringReader(ntriple)); - - verify(mockValueFactory).createLiteral("hello", "en"); - verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockLangLiteral); - } - - @Test - @DisplayName("Test parsing a typed literal") - void testParseTypedLiteral() throws ParsingErrorException { - String ntriple = " \"123\"^^ ."; - parser.parse(new StringReader(ntriple)); - - ArgumentCaptor datatypeCaptor = ArgumentCaptor.forClass(IRI.class); - verify(mockValueFactory).createLiteral(eq("123"), datatypeCaptor.capture()); - verify(mockValueFactory).createIRI("http://www.w3.org/2001/XMLSchema#integer"); - - assertEquals(mockDatatypeIRI, datatypeCaptor.getValue()); - verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockTypedLiteral); - } - - @Test - @DisplayName("Test parsing multiple triples in one go") - void testParseMultipleTriples() throws ParsingErrorException { - String ntriples = - " .\n" + - "_:b2 \"literal\"@fr ."; - parser.parse(new StringReader(ntriples)); - - - verify(mockValueFactory).createIRI("http://example.org/s1"); - verify(mockValueFactory).createIRI("http://example.org/p1"); - verify(mockValueFactory).createIRI("http://example.org/o1"); - verify(mockModel).add(any(IRI.class), any(IRI.class), any(IRI.class)); - - - verify(mockValueFactory).createBNode("b2"); - verify(mockValueFactory).createIRI("http://example.org/p2"); - verify(mockValueFactory).createLiteral("literal", "fr"); - - ArgumentCaptor subjCaptor = ArgumentCaptor.forClass(Resource.class); - ArgumentCaptor predCaptor = ArgumentCaptor.forClass(IRI.class); - ArgumentCaptor objCaptor = ArgumentCaptor.forClass(Value.class); - verify(mockModel, times(2)).add(subjCaptor.capture(), predCaptor.capture(), objCaptor.capture()); - } - - @Test - @DisplayName("Test parsing with comments and empty lines ignored") - void testParseWithCommentsAndEmptyLines() throws ParsingErrorException { - String ntriples = """ - # This is a comment - - . - # Another comment with leading spaces - - """; - - - parser.parse(new StringReader(ntriples)); - - verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI); - verifyNoMoreInteractions(mockModel); - } - - - @ParameterizedTest - @ValueSource(strings = { - " .", - " " - }) - @DisplayName("Invalid N-Triples should throw ParsingErrorException") - void testInvalidNTriplesThrowsParsingErrorException(String invalidNTriple) { - StringReader reader = new StringReader(invalidNTriple); - assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); - } - - @Test - @DisplayName("Test that invalid line (too few parts) throws ParsingErrorException") - void testParseInvalidLineTooFewPartsThrowsException() { - String ntriple = " ."; - StringReader reader = new StringReader(ntriple); - - assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); - } - - @Test - @DisplayName("Test that invalid IRI format throws ParsingErrorException") - void testParseInvalidResourceThrowsException() { - String ntriple = "invalid_subject ."; - Reader reader = new StringReader(ntriple); - - assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); - } - - @Test - @DisplayName("Test that invalid predicate IRI format throws ParsingErrorException") - void testParseInvalidIRITThrowsException() { - String ntriple = " invalid_predicate ."; - StringReader reader = new StringReader(ntriple); - - assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); - } - @Test - @DisplayName("Test that invalid literal format throws ParsingErrorException") - void testParseInvalidLiteralThrowsException() { - String ntriple = " \"unclosed literal ."; - Reader reader = new StringReader(ntriple); - - assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); - } - - @Test - @DisplayName("Test that I/O errors during parsing throw ParsingErrorException") - void testParseIOErrorThrowsException() { - StringReader reader = new StringReader("test line ."); - reader.close(); - - assertThrows(ParsingErrorException.class, () -> parser.parse(reader)); - } -} From 3f85d74a03eeabb933582ce10ba50d726d192c50 Mon Sep 17 00:00:00 2001 From: "AD\\aabdoun" Date: Wed, 9 Jul 2025 15:17:14 +0200 Subject: [PATCH 03/15] =?UTF-8?q?solution=20pour=20garantit=20que=20vos=20?= =?UTF-8?q?fichiers=20g=C3=A9n=C3=A9r=C3=A9s=20auront=20une=20seule=20d?= =?UTF-8?q?=C3=A9claration=20de=20package=20propre=20et=20correcte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index b8e91e7e1..80b394731 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -349,9 +349,8 @@ tasks.withType().configureEach { // Configure the Antlr task to generate parser code with specific arguments tasks.named("generateGrammarSource") { arguments.addAll(listOf("-visitor", "-long-messages", "-package", "fr.inria.corese.core.next.impl.parser.antlr")) - outputDirectory = antlrPackageDir - inputs.files(fileTree("src/main/antlr")) - outputs.dir(antlrPackageDir) + outputDirectory = layout.buildDirectory.dir("generated-src/antlr/main/fr/inria/corese/core/next/impl/parser/antlr").get().asFile + outputs.dirs(outputDirectory) } // Ensure Java compilation depends on both JavaCC and Antlr code generation From 428a90825744b178243d94b114790704ef2aebe8 Mon Sep 17 00:00:00 2001 From: "AD\\aabdoun" Date: Thu, 10 Jul 2025 08:46:11 +0200 Subject: [PATCH 04/15] Ajouter ANTLRNTriplesParser et NTriplesListener --- .../parser/ntriples/ANTLRNTriplesParser.java | 87 +++++- .../io/parser/ntriples/NTriplesListener.java | 261 ++++++++++++++++++ 2 files changed, 347 insertions(+), 1 deletion(-) create mode 100644 src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListener.java diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java index 291d67d9f..4430491d1 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java @@ -1,4 +1,89 @@ package fr.inria.corese.core.next.impl.io.parser.ntriples; -public class ANTLRNTriplesParser { +import fr.inria.corese.core.next.api.Model; +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.base.io.parser.AbstractRDFParser; +import fr.inria.corese.core.next.api.io.IOOptions; +import fr.inria.corese.core.next.impl.exception.ParsingErrorException; +import fr.inria.corese.core.next.impl.parser.antlr.NTriplesLexer; +import fr.inria.corese.core.next.impl.parser.antlr.NTriplesParser; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.ParseTreeListener; +import org.antlr.v4.runtime.tree.ParseTreeWalker; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; + +/** + * An ANTLR4-based parser for N-Triples format. + * This parser uses an ANTLR grammar to tokenize and parse N-Triples documents, + * then a listener to build the RDF model. + */ +public class ANTLRNTriplesParser extends AbstractRDFParser { + + public ANTLRNTriplesParser(Model model, ValueFactory factory) { + super(model, factory); + } + + public ANTLRNTriplesParser(Model model, ValueFactory factory, IOOptions config) { + super(model, factory, config); + } + + @Override + public RdfFormat getRDFFormat() { + return RdfFormat.NTRIPLES; + } + + + @Override + public void parse(InputStream in) throws ParsingErrorException { + parse(new InputStreamReader(in, StandardCharsets.UTF_8), null); + } + + @Override + public void parse(InputStream in, String baseURI) throws ParsingErrorException { + parse(new InputStreamReader(in, StandardCharsets.UTF_8), baseURI); + } + + @Override + public void parse(Reader reader) throws ParsingErrorException { + parse(reader, null); + } + + /** + * Parses N-Triples data from a Reader using ANTLR4. + * + * @param reader The Reader to read RDF data from. + * @param baseURI The base URI (ignored for N-Triples as all URIs are absolute). + * @throws ParsingErrorException if a parsing or I/O error occurs. + */ + @Override + public void parse(Reader reader, String baseURI) throws ParsingErrorException { + try { + CharStream charStream = CharStreams.fromReader(reader); + NTriplesLexer lexer = new NTriplesLexer(charStream); + CommonTokenStream tokens = new CommonTokenStream(lexer); + + NTriplesParser antlrParser = new NTriplesParser(tokens); + ParseTreeWalker walker = new ParseTreeWalker(); + ParseTree tree = antlrParser.ntriplesDoc(); + + + NTriplesListener listener = new NTriplesListener(getModel(), getValueFactory(), getConfig()); + + walker.walk((ParseTreeListener) listener, tree); + + } catch (IOException e) { + throw new ParsingErrorException("Failed to parse N-Triples: " + e.getMessage(), e); + } catch (Exception e) { + throw new ParsingErrorException("Unexpected error during N-Triples parsing: " + e.getMessage(), e); + } + } } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListener.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListener.java new file mode 100644 index 000000000..040dbb6d6 --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListener.java @@ -0,0 +1,261 @@ +package fr.inria.corese.core.next.impl.io.parser.ntriples; + +import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.io.IOOptions; +import fr.inria.corese.core.next.impl.parser.antlr.NTriplesBaseListener; +import fr.inria.corese.core.next.impl.parser.antlr.NTriplesParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Listener for the ANTLR4 generated parser for N-Triples. + * This listener traverses the parse tree and builds the RDF model. + * It includes unescaping logic for URIs and literals. + */ +public class NTriplesListener extends NTriplesBaseListener { + + private static final Logger logger = LoggerFactory.getLogger(NTriplesListener.class); + + private final Model model; + private final ValueFactory factory; + private final IOOptions options; + + private Resource currentSubject; + private IRI currentPredicate; + + public NTriplesListener(Model model, ValueFactory factory, IOOptions options) { + this.model = model; + this.factory = factory; + this.options = options; + } + + @Override + public void enterTriple(NTriplesParser.TripleContext ctx) { + currentSubject = extractSubject(ctx.subject()); + currentPredicate = extractPredicate(ctx.predicate()); + } + + @Override + public void exitTriple(NTriplesParser.TripleContext ctx) { + Value object = extractObject(ctx.object()); + model.add(currentSubject, currentPredicate, object); + currentSubject = null; + currentPredicate = null; + } + + /** + * Extracts a resource (IRI or Blank Node) from the subject context. + */ + protected Resource extractSubject(NTriplesParser.SubjectContext ctx) { + if (ctx.IRIREF() != null) { + return factory.createIRI(unescapeUri(ctx.IRIREF().getText().substring(1, ctx.IRIREF().getText().length() - 1))); + } + if (ctx.BLANK_NODE_LABEL() != null) { + return factory.createBNode(ctx.BLANK_NODE_LABEL().getText().substring(2)); + } + throw new IllegalArgumentException("Unsupported N-Triples subject: " + ctx.getText()); + } + + /** + * Extracts a predicate (IRI) from the predicate context. + */ + protected IRI extractPredicate(NTriplesParser.PredicateContext ctx) { + if (ctx.IRIREF() != null) { + return factory.createIRI(unescapeUri(ctx.IRIREF().getText().substring(1, ctx.IRIREF().getText().length() - 1))); + } + throw new IllegalArgumentException("Unsupported N-Triples predicate: " + ctx.getText()); + } + + /** + * Extracts a value (IRI, Blank Node, or Literal) from the object context. + */ + protected Value extractObject(NTriplesParser.ObjectContext ctx) { + if (ctx.IRIREF() != null) { + return factory.createIRI(unescapeUri(ctx.IRIREF().getText().substring(1, ctx.IRIREF().getText().length() - 1))); + } + if (ctx.BLANK_NODE_LABEL() != null) { + return factory.createBNode(ctx.BLANK_NODE_LABEL().getText().substring(2)); + } + if (ctx.literal() != null) { + return extractLiteral(ctx.literal()); + } + throw new IllegalArgumentException("Unsupported N-Triples object: " + ctx.getText()); + } + + /** + * Extracts and unescapes a literal from the ANTLR context. + * This method handles string literals with or without datatype/language. + */ + protected Literal extractLiteral(NTriplesParser.LiteralContext ctx) { + String label = ctx.STRING_LITERAL_QUOTE().getText(); + label = unescapeLiteral(label); + + if (ctx.IRIREF() != null) { + IRI datatype = factory.createIRI(unescapeUri(ctx.IRIREF().getText().substring(1, ctx.IRIREF().getText().length() - 1))); + return factory.createLiteral(label, datatype); + } + if (ctx.LANGTAG() != null) { + String lang = ctx.LANGTAG().getText().substring(1); + return factory.createLiteral(label, lang); + } + return factory.createLiteral(label); + } + + /** + * Unescapes common N-Triples literal escape sequences. + * This method handles `\"`, `\\`, `\n`, `\t`, `\r`, `\b`, `\f`. + * It also handles `\ uXXXX` and `\UXXXXXXXX` for Unicode escapes. + * It also removes the surrounding quotes from the literal string. + * + * @param literalText The raw literal string from ANTLR (including quotes and escapes). + * @return The unescaped literal string without surrounding quotes. + */ + protected String unescapeLiteral(String literalText) { + String unquotedLiteral = literalText.substring(1, literalText.length() - 1); + + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < unquotedLiteral.length(); i++) { + char c = unquotedLiteral.charAt(i); + if (c == '\\' && i + 1 < unquotedLiteral.length()) { + char nextChar = unquotedLiteral.charAt(i + 1); + switch (nextChar) { + case '"': + builder.append('"'); + i++; + break; + case '\\': + builder.append('\\'); + i++; + break; + case 'n': + builder.append('\n'); + i++; + break; + case 't': + builder.append('\t'); + i++; + break; + case 'r': + builder.append('\r'); + i++; + break; + case 'b': + builder.append('\b'); + i++; + break; + case 'f': + builder.append('\f'); + i++; + break; + case 'u': + if (i + 5 < unquotedLiteral.length()) { + String hex = unquotedLiteral.substring(i + 2, i + 6); + try { + int unicodeChar = Integer.parseInt(hex, 16); + builder.append((char) unicodeChar); + i += 5; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid \\uXXXX escape sequence in literal: \\u" + hex); + } + } else { + throw new IllegalArgumentException("Incomplete \\uXXXX escape sequence in literal: " + unquotedLiteral.substring(i)); + } + break; + case 'U': + if (i + 9 < unquotedLiteral.length()) { + String hex = unquotedLiteral.substring(i + 2, i + 10); + try { + int unicodeChar = Integer.parseInt(hex, 16); + if (Character.isSupplementaryCodePoint(unicodeChar)) { + builder.append(Character.highSurrogate(unicodeChar)); + builder.append(Character.lowSurrogate(unicodeChar)); + } else { + builder.append((char) unicodeChar); + } + i += 9; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid \\UXXXXXXXX escape sequence in literal: \\U" + hex); + } + } else { + throw new IllegalArgumentException("Incomplete \\UXXXXXXXX escape sequence in literal: " + unquotedLiteral.substring(i)); + } + break; + default: + builder.append(c).append(nextChar); + i++; + break; + } + } else { + builder.append(c); + } + } + return builder.toString(); + } + + /** + * Unescapes common N-Triples URI escape sequences. + * This method handles `\>`, `\\`, `\ uXXXX`, `\UXXXXXXXX`. + * + * @param uri The escaped URI string. + * @return The unescaped URI string. + */ + protected String unescapeUri(String uri) { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < uri.length(); i++) { + char c = uri.charAt(i); + if (c == '\\' && i + 1 < uri.length()) { + char nextChar = uri.charAt(i + 1); + switch (nextChar) { + case '>': + builder.append('>'); + i++; + break; + case '\\': + builder.append('\\'); + i++; + break; + case 'u': + if (i + 5 < uri.length()) { + String hex = uri.substring(i + 2, i + 6); + try { + int unicodeChar = Integer.parseInt(hex, 16); + builder.append((char) unicodeChar); + i += 5; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid \\uXXXX escape sequence in URI: \\u" + hex); + } + } else { + throw new IllegalArgumentException("Incomplete \\uXXXX escape sequence in URI: " + uri.substring(i)); + } + break; + case 'U': + if (i + 9 < uri.length()) { + String hex = uri.substring(i + 2, i + 10); + try { + int unicodeChar = Integer.parseInt(hex, 16); + if (Character.isSupplementaryCodePoint(unicodeChar)) { + builder.append(Character.highSurrogate(unicodeChar)); + builder.append(Character.lowSurrogate(unicodeChar)); + } else { + builder.append((char) unicodeChar); + } + i += 9; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid \\UXXXXXXXX escape sequence in URI: \\U" + hex); + } + } else { + throw new IllegalArgumentException("Incomplete \\UXXXXXXXX escape sequence in URI: " + uri.substring(i)); + } + break; + default: + builder.append(c).append(nextChar); + i++; + break; + } + } else { + builder.append(c); + } + } + return builder.toString(); + } +} From 31d8c7e9bd22fc260d3c26ccb25b7de0dcd5b33b Mon Sep 17 00:00:00 2001 From: "AD\\aabdoun" Date: Thu, 10 Jul 2025 13:43:08 +0200 Subject: [PATCH 05/15] Ajouter ANTLRNQuadsParser --- .../next/impl/io/parser/ParserFactory.java | 5 ++ .../io/parser/nquads/ANTLRNQuadsParser.java | 88 +++++++++++++++++++ .../impl/io/parser/nquads/NQuadsListener.java | 4 + .../impl/io/parser/ParserFactoryTest.java | 4 + .../parser/nquads/ANTLRNQuadsParserTest.java | 4 + .../io/parser/nquads/NQuadsListenerTest.java | 4 + 6 files changed, 109 insertions(+) create mode 100644 src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParser.java create mode 100644 src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java create mode 100644 src/test/java/fr/inria/corese/core/next/impl/io/parser/ParserFactoryTest.java create mode 100644 src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParserTest.java create mode 100644 src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListenerTest.java diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ParserFactory.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ParserFactory.java index 1e0cfdf06..dce59a80b 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ParserFactory.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ParserFactory.java @@ -7,6 +7,7 @@ import fr.inria.corese.core.next.api.io.parser.RDFParser; import fr.inria.corese.core.next.api.io.parser.RDFParserOptions; import fr.inria.corese.core.next.impl.io.parser.jsonld.JSONLDParser; +import fr.inria.corese.core.next.impl.io.parser.nquads.ANTLRNQuadsParser; import fr.inria.corese.core.next.impl.io.parser.ntriples.ANTLRNTriplesParser; import fr.inria.corese.core.next.impl.io.parser.turtle.ANTLRTurtleParser; @@ -41,6 +42,8 @@ public RDFParser createRDFParser(RDFFormat format, Model model, ValueFactory fac return new ANTLRTurtleParser(model, factory, config); } else if (format == RdfFormat.NTRIPLES) { return new ANTLRNTriplesParser(model, factory, config); + } else if (format == RdfFormat.NQUADS) { + return new ANTLRNQuadsParser(model, factory, config); } throw new IllegalArgumentException("Unsupported format: " + format); } @@ -60,6 +63,8 @@ public RDFParser createRDFParser(RDFFormat format, Model model, ValueFactory fac return new ANTLRTurtleParser(model, factory); } else if (format == RdfFormat.NTRIPLES) { return new ANTLRNTriplesParser(model, factory); + } else if (format == RdfFormat.NQUADS) { + return new ANTLRNQuadsParser(model, factory); } throw new IllegalArgumentException("Unsupported format: " + format); } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParser.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParser.java new file mode 100644 index 000000000..8831352fe --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParser.java @@ -0,0 +1,88 @@ +package fr.inria.corese.core.next.impl.io.parser.nquads; + +import fr.inria.corese.core.next.api.Model; +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.base.io.parser.AbstractRDFParser; +import fr.inria.corese.core.next.api.io.IOOptions; +import fr.inria.corese.core.next.impl.exception.ParsingErrorException; +import fr.inria.corese.core.next.impl.parser.antlr.NQuadsLexer; +import fr.inria.corese.core.next.impl.parser.antlr.NQuadsParser; +import org.antlr.v4.runtime.CharStream; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTree; +import org.antlr.v4.runtime.tree.ParseTreeListener; +import org.antlr.v4.runtime.tree.ParseTreeWalker; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; + +/** + * An ANTLR4-based parser for N-Quads format. + * This parser uses an ANTLR grammar to tokenize and parse N-Quads documents, + * then a listener to build the RDF model. + */ +public class ANTLRNQuadsParser extends AbstractRDFParser { + + public ANTLRNQuadsParser(Model model, ValueFactory factory) { + super(model, factory); + } + + public ANTLRNQuadsParser(Model model, ValueFactory factory, IOOptions config) { + super(model, factory, config); + } + + @Override + public RdfFormat getRDFFormat() { + return RdfFormat.NQUADS; + } + + + @Override + public void parse(InputStream in) throws ParsingErrorException { + parse(new InputStreamReader(in, StandardCharsets.UTF_8), null); + } + + @Override + public void parse(InputStream in, String baseURI) throws ParsingErrorException { + parse(new InputStreamReader(in, StandardCharsets.UTF_8), baseURI); + } + + @Override + public void parse(Reader reader) throws ParsingErrorException { + parse(reader, null); + } + + /** + * Parses N-Quads data from a Reader using ANTLR4. + * + * @param reader The Reader to read RDF data from. + * @param baseURI The base URI (ignored for N-Quads as all URIs are absolute). + * @throws ParsingErrorException if a parsing or I/O error occurs. + */ + @Override + public void parse(Reader reader, String baseURI) throws ParsingErrorException { + try { + CharStream charStream = CharStreams.fromReader(reader); + NQuadsLexer lexer = new NQuadsLexer(charStream); + CommonTokenStream tokens = new CommonTokenStream(lexer); + + NQuadsParser antlrParser = new NQuadsParser(tokens); + ParseTreeWalker walker = new ParseTreeWalker(); + ParseTree tree = antlrParser.nquadsDoc(); + + NQuadsListener listener = new NQuadsListener(getModel(), getValueFactory(), getConfig()); + + walker.walk((ParseTreeListener) listener, tree); + + } catch (IOException e) { + throw new ParsingErrorException("Failed to parse N-Quads: " + e.getMessage(), e); + } catch (Exception e) { + throw new ParsingErrorException("Unexpected error during N-Quads parsing: " + e.getMessage(), e); + } + } +} diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java new file mode 100644 index 000000000..4e6cf242a --- /dev/null +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java @@ -0,0 +1,4 @@ +package fr.inria.corese.core.next.impl.io.parser.nquads; + +public class NQuadsListener { +} diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ParserFactoryTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ParserFactoryTest.java new file mode 100644 index 000000000..8f74372af --- /dev/null +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ParserFactoryTest.java @@ -0,0 +1,4 @@ +package fr.inria.corese.core.next.impl.io.parser; + +public class ParserFactoryTest { +} diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParserTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParserTest.java new file mode 100644 index 000000000..decc1569e --- /dev/null +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParserTest.java @@ -0,0 +1,4 @@ +package fr.inria.corese.core.next.impl.io.parser.nquads; + +public class ANTLRNQuadsParserTest { +} diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListenerTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListenerTest.java new file mode 100644 index 000000000..4ea763faf --- /dev/null +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListenerTest.java @@ -0,0 +1,4 @@ +package fr.inria.corese.core.next.impl.io.parser.nquads; + +public class NQuadsListenerTest { +} From 5ff60c8021891a6e933697a116938b73f1fc3ba0 Mon Sep 17 00:00:00 2001 From: "AD\\aabdoun" Date: Fri, 11 Jul 2025 14:44:40 +0200 Subject: [PATCH 06/15] Ajouter NQuadsListener et paser FactoryTest --- .../impl/io/parser/nquads/NQuadsListener.java | 286 +++++++++++++++++- .../impl/io/parser/ParserFactoryTest.java | 110 ++++++- 2 files changed, 394 insertions(+), 2 deletions(-) diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java index 4e6cf242a..db55935c9 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java @@ -1,4 +1,288 @@ package fr.inria.corese.core.next.impl.io.parser.nquads; -public class NQuadsListener { +import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.io.IOOptions; +import fr.inria.corese.core.next.impl.parser.antlr.NQuadsBaseListener; +import fr.inria.corese.core.next.impl.parser.antlr.NQuadsParser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Listener for the ANTLR4 generated parser for N-Quads. + * This listener traverses the parse tree and builds the RDF model, + * supporting named graphs. It includes unescaping logic for URIs and literals. + */ +public class NQuadsListener extends NQuadsBaseListener { + + private static final Logger logger = LoggerFactory.getLogger(NQuadsListener.class); + + private final Model model; + private final ValueFactory factory; + private final IOOptions options; + + private Resource currentSubject; + private IRI currentPredicate; + private Resource currentGraph; + + public NQuadsListener(Model model, ValueFactory factory, IOOptions options) { + this.model = model; + this.factory = factory; + this.options = options; + } + + @Override + public void enterStatement(NQuadsParser.StatementContext ctx) { + + currentSubject = extractSubject(ctx.subject()); + currentPredicate = extractPredicate(ctx.predicate()); + if (ctx.graphLabel() != null) { + currentGraph = extractGraph(ctx.graphLabel()); + } else { + currentGraph = null; + } + } + + @Override + public void exitStatement(NQuadsParser.StatementContext ctx) { + + Value object = extractObject(ctx.object()); + if (currentGraph != null) { + model.add(currentSubject, currentPredicate, object, currentGraph); + } else { + model.add(currentSubject, currentPredicate, object); + } + currentSubject = null; + currentPredicate = null; + currentGraph = null; + } + + /** + * Extracts a resource (IRI or Blank Node) from the subject context. + */ + protected Resource extractSubject(NQuadsParser.SubjectContext ctx) { + if (ctx.IRIREF() != null) { + return factory.createIRI(unescapeUri(ctx.IRIREF().getText().substring(1, ctx.IRIREF().getText().length() - 1))); + } + if (ctx.BLANK_NODE_LABEL() != null) { + return factory.createBNode(ctx.BLANK_NODE_LABEL().getText().substring(2)); + } + throw new IllegalArgumentException("Unsupported N-Quads subject: " + ctx.getText()); + } + + /** + * Extracts a predicate (IRI) from the predicate context. + */ + protected IRI extractPredicate(NQuadsParser.PredicateContext ctx) { + if (ctx.IRIREF() != null) { + return factory.createIRI(unescapeUri(ctx.IRIREF().getText().substring(1, ctx.IRIREF().getText().length() - 1))); + } + throw new IllegalArgumentException("Unsupported N-Quads predicate: " + ctx.getText()); + } + + /** + * Extracts a value (IRI, Blank Node, or Literal) from the object context. + */ + protected Value extractObject(NQuadsParser.ObjectContext ctx) { + if (ctx.IRIREF() != null) { + return factory.createIRI(unescapeUri(ctx.IRIREF().getText().substring(1, ctx.IRIREF().getText().length() - 1))); + } + if (ctx.BLANK_NODE_LABEL() != null) { + return factory.createBNode(ctx.BLANK_NODE_LABEL().getText().substring(2)); + } + if (ctx.literal() != null) { + return extractLiteral(ctx.literal()); + } + throw new IllegalArgumentException("Unsupported N-Quads object: " + ctx.getText()); + } + + /** + * Extracts a graph (IRI or Blank Node) from the graph context. + */ + protected Resource extractGraph(NQuadsParser.GraphLabelContext ctx) { + if (ctx.IRIREF() != null) { + return factory.createIRI(unescapeUri(ctx.IRIREF().getText().substring(1, ctx.IRIREF().getText().length() - 1))); + } + if (ctx.BLANK_NODE_LABEL() != null) { + return factory.createBNode(ctx.BLANK_NODE_LABEL().getText().substring(2)); + } + throw new IllegalArgumentException("Unsupported N-Quads graph: " + ctx.getText()); + } + + /** + * Extracts and unescapes a literal from the ANTLR context. + * This method handles string literals with or without datatype/language. + */ + protected Literal extractLiteral(NQuadsParser.LiteralContext ctx) { + String label = ctx.STRING_LITERAL_QUOTE().getText(); + label = unescapeLiteral(label); + + if (ctx.IRIREF() != null) { + IRI datatype = factory.createIRI(unescapeUri(ctx.IRIREF().getText().substring(1, ctx.IRIREF().getText().length() - 1))); + return factory.createLiteral(label, datatype); + } + if (ctx.LANGTAG() != null) { + String lang = ctx.LANGTAG().getText().substring(1); + return factory.createLiteral(label, lang); + } + + return factory.createLiteral(label); + } + + /** + * Unescapes common N-Quads literal escape sequences. + * This method handles `\"`, `\\`, `\n`, `\t`, `\r`, `\b`, `\f`. + * It also handles `\ uXXXX` and `\UXXXXXXXX` for Unicode escapes. + * It also removes the surrounding quotes from the literal string. + * + * @param literalText The raw literal string from ANTLR (including quotes and escapes). + * @return The unescaped literal string without surrounding quotes. + */ + protected String unescapeLiteral(String literalText) { + String unquotedLiteral = literalText.substring(1, literalText.length() - 1); + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < unquotedLiteral.length(); i++) { + char c = unquotedLiteral.charAt(i); + if (c == '\\' && i + 1 < unquotedLiteral.length()) { + char nextChar = unquotedLiteral.charAt(i + 1); + switch (nextChar) { + case '"': + sb.append('"'); + i++; + break; + case '\\': + sb.append('\\'); + i++; + break; + case 'n': + sb.append('\n'); + i++; + break; + case 't': + sb.append('\t'); + i++; + break; + case 'r': + sb.append('\r'); + i++; + break; + case 'b': + sb.append('\b'); + i++; + break; + case 'f': + sb.append('\f'); + i++; + break; + case 'u': + if (i + 5 < unquotedLiteral.length()) { + String hex = unquotedLiteral.substring(i + 2, i + 6); + try { + int unicodeChar = Integer.parseInt(hex, 16); + sb.append((char) unicodeChar); + i += 5; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid \\uXXXX escape sequence in literal: \\u" + hex); + } + } else { + throw new IllegalArgumentException("Incomplete \\uXXXX escape sequence in literal: " + unquotedLiteral.substring(i)); + } + break; + case 'U': + if (i + 9 < unquotedLiteral.length()) { + String hex = unquotedLiteral.substring(i + 2, i + 10); + try { + int unicodeChar = Integer.parseInt(hex, 16); + if (Character.isSupplementaryCodePoint(unicodeChar)) { + sb.append(Character.highSurrogate(unicodeChar)); + sb.append(Character.lowSurrogate(unicodeChar)); + } else { + sb.append((char) unicodeChar); + } + i += 9; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid \\UXXXXXXXX escape sequence in literal: \\U" + hex); + } + } else { + throw new IllegalArgumentException("Incomplete \\UXXXXXXXX escape sequence in literal: " + unquotedLiteral.substring(i)); + } + break; + default: + sb.append(c).append(nextChar); + i++; + break; + } + } else { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * Unescapes common N-Quads URI escape sequences. + * This method handles `\>`, `\\`, `\ uXXXX`, `\UXXXXXXXX`. + * + * @param uri The escaped URI string. + * @return The unescaped URI string. + */ + protected String unescapeUri(String uri) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < uri.length(); i++) { + char c = uri.charAt(i); + if (c == '\\' && i + 1 < uri.length()) { + char nextChar = uri.charAt(i + 1); + switch (nextChar) { + case '>': + sb.append('>'); + i++; + break; + case '\\': + sb.append('\\'); + i++; + break; + case 'u': + if (i + 5 < uri.length()) { + String hex = uri.substring(i + 2, i + 6); + try { + int unicodeChar = Integer.parseInt(hex, 16); + sb.append((char) unicodeChar); + i += 5; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid \\uXXXX escape sequence in URI: \\u" + hex); + } + } else { + throw new IllegalArgumentException("Incomplete \\uXXXX escape sequence in URI: " + uri.substring(i)); + } + break; + case 'U': + if (i + 9 < uri.length()) { + String hex = uri.substring(i + 2, i + 10); + try { + int unicodeChar = Integer.parseInt(hex, 16); + if (Character.isSupplementaryCodePoint(unicodeChar)) { + sb.append(Character.highSurrogate(unicodeChar)); + sb.append(Character.lowSurrogate(unicodeChar)); + } else { + sb.append((char) unicodeChar); + } + i += 9; + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Invalid \\UXXXXXXXX escape sequence in URI: \\U" + hex); + } + } else { + throw new IllegalArgumentException("Incomplete \\UXXXXXXXX escape sequence in URI: " + uri.substring(i)); + } + break; + default: + sb.append(c).append(nextChar); + i++; + break; + } + } else { + sb.append(c); + } + } + return sb.toString(); + } } diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ParserFactoryTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ParserFactoryTest.java index 8f74372af..66dce0f32 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ParserFactoryTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ParserFactoryTest.java @@ -1,4 +1,112 @@ package fr.inria.corese.core.next.impl.io.parser; -public class ParserFactoryTest { +import fr.inria.corese.core.next.api.Model; +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.parser.RDFParserOptions; +import fr.inria.corese.core.next.impl.io.parser.jsonld.JSONLDParser; +import fr.inria.corese.core.next.impl.io.parser.nquads.ANTLRNQuadsParser; +import fr.inria.corese.core.next.impl.io.parser.ntriples.ANTLRNTriplesParser; +import fr.inria.corese.core.next.impl.io.parser.turtle.ANTLRTurtleParser; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for the ParserFactory class. + * This class verifies that the factory correctly instantiates the appropriate + * RDFParser implementation based on the provided RdfFormat. + */ +@ExtendWith(MockitoExtension.class) +class ParserFactoryTest { + + private ParserFactory parserFactory; + + @Mock + private Model mockModel; + + @Mock + private ValueFactory mockValueFactory; + + @Mock + private RDFParserOptions mockParserOptions; + + + @BeforeEach + void setUp() { + parserFactory = new ParserFactory(); + } + + @Test + @DisplayName("createRDFParser (with config) should return JSONLDParser for JSONLD format") + void testCreateRDFParserWithConfig_JSONLD() { + RDFParser parser = parserFactory.createRDFParser(RdfFormat.JSONLD, mockModel, mockValueFactory, mockParserOptions); + assertNotNull(parser); + assertTrue(parser instanceof JSONLDParser); + } + + @Test + @DisplayName("createRDFParser (with config) should return ANTLRTurtleParser for TURTLE format") + void testCreateRDFParserWithConfig_TURTLE() { + RDFParser parser = parserFactory.createRDFParser(RdfFormat.TURTLE, mockModel, mockValueFactory, mockParserOptions); + assertNotNull(parser); + assertTrue(parser instanceof ANTLRTurtleParser); + } + + @Test + @DisplayName("createRDFParser (with config) should return ANTLRNTriplesParser for N-TRIPLES format") + void testCreateRDFParserWithConfig_NTRIPLES() { + RDFParser parser = parserFactory.createRDFParser(RdfFormat.NTRIPLES, mockModel, mockValueFactory, mockParserOptions); + assertNotNull(parser); + assertTrue(parser instanceof ANTLRNTriplesParser); + } + + @Test + @DisplayName("createRDFParser (with config) should return ANTLRNQuadsParser for N-QUADS format") + void testCreateRDFParserWithConfig_NQUADS() { + RDFParser parser = parserFactory.createRDFParser(RdfFormat.NQUADS, mockModel, mockValueFactory, mockParserOptions); + assertNotNull(parser); + assertTrue(parser instanceof ANTLRNQuadsParser); + } + + + @Test + @DisplayName("createRDFParser (without config) should return JSONLDParser for JSONLD format") + void testCreateRDFParserWithoutConfig_JSONLD() { + RDFParser parser = parserFactory.createRDFParser(RdfFormat.JSONLD, mockModel, mockValueFactory); + assertNotNull(parser); + assertTrue(parser instanceof JSONLDParser); + } + + @Test + @DisplayName("createRDFParser (without config) should return ANTLRTurtleParser for TURTLE format") + void testCreateRDFParserWithoutConfig_TURTLE() { + RDFParser parser = parserFactory.createRDFParser(RdfFormat.TURTLE, mockModel, mockValueFactory); + assertNotNull(parser); + assertTrue(parser instanceof ANTLRTurtleParser); + } + + @Test + @DisplayName("createRDFParser (without config) should return ANTLRNTriplesParser for N-TRIPLES format") + void testCreateRDFParserWithoutConfig_NTRIPLES() { + RDFParser parser = parserFactory.createRDFParser(RdfFormat.NTRIPLES, mockModel, mockValueFactory); + assertNotNull(parser); + assertTrue(parser instanceof ANTLRNTriplesParser); + } + + @Test + @DisplayName("createRDFParser (without config) should return ANTLRNQuadsParser for N-QUADS format") + void testCreateRDFParserWithoutConfig_NQUADS() { + RDFParser parser = parserFactory.createRDFParser(RdfFormat.NQUADS, mockModel, mockValueFactory); + assertNotNull(parser); + assertTrue(parser instanceof ANTLRNQuadsParser); + } + } From 25cfd8964bd0120f290733240944f4a725d58daa Mon Sep 17 00:00:00 2001 From: "AD\\aabdoun" Date: Tue, 15 Jul 2025 14:59:45 +0200 Subject: [PATCH 07/15] TU N-Triples --- .../ntriples/ANTLRNTriplesParserTest.java | 209 +++++++++++++ .../parser/ntriples/NTriplesListenerTest.java | 280 ++++++++++++++++++ 2 files changed, 489 insertions(+) create mode 100644 src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParserTest.java create mode 100644 src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListenerTest.java diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParserTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParserTest.java new file mode 100644 index 000000000..083ec4c23 --- /dev/null +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParserTest.java @@ -0,0 +1,209 @@ +package fr.inria.corese.core.next.impl.io.parser.ntriples; + +import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.base.io.RdfFormat; +import fr.inria.corese.core.next.impl.exception.ParsingErrorException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.StringReader; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +/** + * Unit tests for the ANTLRNTriplesParser class. + * These tests verify the parser's ability to correctly parse N-Triples + * and interact with the Model and ValueFactory, including error handling + * and unescaping of IRIs and literals. + */ +@ExtendWith(MockitoExtension.class) +class ANTLRNTriplesParserTest { + + @Mock + private Model mockModel; + + @Mock + private ValueFactory mockValueFactory; + + private ANTLRNTriplesParser parser; + + @Mock + private IRI mockSubjectIRI; + @Mock + private IRI mockPredicateIRI; + @Mock + private IRI mockObjectIRI; + @Mock + private BNode mockSubjectBNode; + @Mock + private BNode mockObjectBNode; + @Mock + private Literal mockSimpleLiteral; + @Mock + private Literal mockLangLiteral; + @Mock + private Literal mockTypedLiteral; + @Mock + private IRI mockDatatypeIRI; + @Mock + private IRI mockEscapedIRI; + @Mock + private Literal mockEscapedLiteral; + + + @BeforeEach + void setUp() { + parser = new ANTLRNTriplesParser(mockModel, mockValueFactory); + + lenient().when(mockValueFactory.createIRI(anyString())).thenAnswer(invocation -> { + String uri = invocation.getArgument(0); + if (uri.equals("http://example.org/subject")) return mockSubjectIRI; + if (uri.equals("http://example.org/predicate")) return mockPredicateIRI; + if (uri.equals("http://example.org/object")) return mockObjectIRI; + if (uri.equals("http://www.w3.org/2001/XMLSchema#integer")) return mockDatatypeIRI; + if (uri.equals("http://example.org/escaped>uri")) return mockEscapedIRI; + return mock(IRI.class); + }); + + lenient().when(mockValueFactory.createBNode(anyString())).thenAnswer(invocation -> { + String label = invocation.getArgument(0); + if (label.equals("sub1")) return mockSubjectBNode; + if (label.equals("obj1")) return mockObjectBNode; + return mock(BNode.class); + }); + + lenient().when(mockValueFactory.createLiteral(eq("simple string"))).thenReturn(mockSimpleLiteral); + lenient().when(mockValueFactory.createLiteral(eq("hello"), eq("en"))).thenReturn(mockLangLiteral); + lenient().when(mockValueFactory.createLiteral(eq("123"), any(IRI.class))).thenReturn(mockTypedLiteral); + lenient().when(mockValueFactory.createLiteral(eq("literal with \"quotes\" and \n newline"))).thenReturn(mockEscapedLiteral); + } + + @Test + @DisplayName("Test get RDF format returns NTRIPLES") + void testGetRDFFormat() { + assertEquals(RdfFormat.NTRIPLES, parser.getRDFFormat()); + } + + @Test + @DisplayName("Test parsing a basic triple with IRIs") + void testParseBasicTriple() throws ParsingErrorException { + String ntriple = " ."; + parser.parse(new StringReader(ntriple)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("Test parsing a triple with a blank node subject") + void testParseBlankNodeSubject() throws ParsingErrorException { + String ntriple = "_:sub1 ."; + parser.parse(new StringReader(ntriple)); + + verify(mockModel).add(mockSubjectBNode, mockPredicateIRI, mockObjectIRI); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("Test parsing a triple with a blank node object") + void testParseBlankNodeObject() throws ParsingErrorException { + String ntriple = " _:obj1 ."; + parser.parse(new StringReader(ntriple)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectBNode); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("Test parsing a triple with a simple literal object") + void testParseSimpleLiteralObject() throws ParsingErrorException { + String ntriple = " \"simple string\" ."; + parser.parse(new StringReader(ntriple)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockSimpleLiteral); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("Test parsing a triple with a language-tagged literal object") + void testParseLangLiteralObject() throws ParsingErrorException { + String ntriple = " \"hello\"@en ."; + parser.parse(new StringReader(ntriple)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockLangLiteral); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("Test parsing a triple with a typed literal object") + void testParseTypedLiteralObject() throws ParsingErrorException { + String ntriple = " \"123\"^^ ."; + parser.parse(new StringReader(ntriple)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockTypedLiteral); + verifyNoMoreInteractions(mockModel); + } + + + @Test + @DisplayName("Test parsing a triple with escaped characters in literal") + void testParseEscapedLiteral() throws ParsingErrorException { + String ntriple = " \"literal with \\\"quotes\\\" and \\n newline\" ."; + parser.parse(new StringReader(ntriple)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockEscapedLiteral); + verifyNoMoreInteractions(mockModel); + } + + + @Test + @DisplayName("Test parsing a triple with Unicode escape in literal (\\uXXXX)") + void testParseUnicodeEscapeLiteralUxxxx() throws ParsingErrorException { + String ntriple = " \"Hello\\u0020World\" ."; + + Literal expectedLiteral = mock(Literal.class); + lenient().when(mockValueFactory.createLiteral(eq("Hello World"))).thenReturn(expectedLiteral); + + parser.parse(new StringReader(ntriple)); + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, expectedLiteral); + } + + @Test + @DisplayName("Test parsing a triple with Unicode escape in literal (\\UXXXXXXXX)") + void testParseUnicodeEscapeLiteral() throws ParsingErrorException { + String ntriple = " \"Euro\\U000020AC\" ."; + + Literal expectedLiteral = mock(Literal.class); + lenient().when(mockValueFactory.createLiteral(eq("Euro€"))).thenReturn(expectedLiteral); + + parser.parse(new StringReader(ntriple)); + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, expectedLiteral); + } + + @Test + @DisplayName("Test parsing a triple with Unicode escape in IRI (\\uXXXX)") + void testParseUnicodeEscapeIRIUxxxx() throws ParsingErrorException { + String ntriple = " ."; + IRI expectedSubjectIRI = mock(IRI.class); + lenient().when(mockValueFactory.createIRI(eq("http://example.org/s ubject"))).thenReturn(expectedSubjectIRI); + + parser.parse(new StringReader(ntriple)); + verify(mockModel).add(expectedSubjectIRI, mockPredicateIRI, mockObjectIRI); + } + + @Test + @DisplayName("Test parsing a triple with Unicode escape in IRI (\\UXXXXXXXX)") + void testParseUnicodeEscapeIRIU() throws ParsingErrorException { + String ntriple = " ."; + IRI expectedSubjectIRI = mock(IRI.class); + lenient().when(mockValueFactory.createIRI(eq("http://example.org/path/€"))).thenReturn(expectedSubjectIRI); + + parser.parse(new StringReader(ntriple)); + verify(mockModel).add(expectedSubjectIRI, mockPredicateIRI, mockObjectIRI); + } +} diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListenerTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListenerTest.java new file mode 100644 index 000000000..b573df6d1 --- /dev/null +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListenerTest.java @@ -0,0 +1,280 @@ +package fr.inria.corese.core.next.impl.io.parser.ntriples; + +import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.io.IOOptions; +import fr.inria.corese.core.next.impl.parser.antlr.NTriplesParser; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + +/** + * Unit tests for the NTriplesListenerImpl class. + * These tests verify that the listener correctly processes ANTLR parse tree contexts + * to extract and unescape RDF terms (IRIs, Blank Nodes, Literals) and add them to the model. + */ +@ExtendWith(MockitoExtension.class) +class NTriplesListenerTest { + + @Mock + private Model mockModel; + + @Mock + private ValueFactory mockValueFactory; + + @Mock + private IOOptions mockIOOptions; + + private NTriplesListener listener; + + @Mock + private IRI mockIRI; + @Mock + private BNode mockBNode; + @Mock + private Literal mockLiteral; + @Mock + private IRI mockDatatypeIRI; + + @BeforeEach + void setUp() { + listener = new NTriplesListener(mockModel, mockValueFactory, mockIOOptions); + + lenient().when(mockValueFactory.createIRI(anyString())).thenAnswer(invocation -> { + String uri = invocation.getArgument(0); + if (uri.equals("http://example.org/test")) return mockIRI; + if (uri.equals("http://example.org/datatype")) return mockDatatypeIRI; + if (uri.equals("http://example.org/escaped>iri")) return mock(IRI.class); + if (uri.equals("http://example.org/s ubject")) return mock(IRI.class); + if (uri.equals("http://example.org/path/€")) return mock(IRI.class); + return mock(IRI.class); + }); + + lenient().when(mockValueFactory.createBNode(anyString())).thenAnswer(invocation -> { + String label = invocation.getArgument(0); + if (label.equals("b1")) return mockBNode; + return mock(BNode.class); + }); + + lenient().when(mockValueFactory.createLiteral(anyString())).thenAnswer(invocation -> { + String value = invocation.getArgument(0); + if (value.equals("test literal")) return mockLiteral; + if (value.equals("literal with \"quotes\" and \n newline")) return mock(Literal.class); + if (value.equals("Hello World")) return mock(Literal.class); + if (value.equals("Euro€")) return mock(Literal.class); + return mock(Literal.class); + }); + lenient().when(mockValueFactory.createLiteral(anyString(), any(IRI.class))).thenReturn(mock(Literal.class)); + lenient().when(mockValueFactory.createLiteral(anyString(), anyString())).thenReturn(mock(Literal.class)); + } + + private TerminalNode mockTerminalNode(String text) { + TerminalNode node = mock(TerminalNode.class); + when(node.getText()).thenReturn(text); + return node; + } + + private T mockRuleContext(Class clazz) { + return mock(clazz); + } + + @Test + @DisplayName("enterTriple and exitTriple should add a triple to the model") + void testEnterExitTripleAddsToModel() { + NTriplesParser.TripleContext tripleCtx = mockRuleContext(NTriplesParser.TripleContext.class); + NTriplesParser.SubjectContext subjectCtx = mockRuleContext(NTriplesParser.SubjectContext.class); + NTriplesParser.PredicateContext predicateCtx = mockRuleContext(NTriplesParser.PredicateContext.class); + NTriplesParser.ObjectContext objectCtx = mockRuleContext(NTriplesParser.ObjectContext.class); + + TerminalNode subjectIriRef = mockTerminalNode(""); + when(subjectCtx.IRIREF()).thenReturn(subjectIriRef); + + TerminalNode predicateIriRef = mockTerminalNode(""); + when(predicateCtx.IRIREF()).thenReturn(predicateIriRef); + + TerminalNode objectIriRef = mockTerminalNode(""); + when(objectCtx.IRIREF()).thenReturn(objectIriRef); + + when(tripleCtx.subject()).thenReturn(subjectCtx); + when(tripleCtx.predicate()).thenReturn(predicateCtx); + when(tripleCtx.object()).thenReturn(objectCtx); + + IRI actualSubjectIRI = mock(IRI.class); + IRI actualPredicateIRI = mock(IRI.class); + IRI actualObjectIRI = mock(IRI.class); + when(mockValueFactory.createIRI("http://example.org/subject")).thenReturn(actualSubjectIRI); + when(mockValueFactory.createIRI("http://example.org/predicate")).thenReturn(actualPredicateIRI); + when(mockValueFactory.createIRI("http://example.org/object")).thenReturn(actualObjectIRI); + + + listener.enterTriple(tripleCtx); + listener.exitTriple(tripleCtx); + + verify(mockModel).add(actualSubjectIRI, actualPredicateIRI, actualObjectIRI); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("extractSubject should return IRI for IRIREF context") + void testExtractSubjectIRI() { + NTriplesParser.SubjectContext ctx = mockRuleContext(NTriplesParser.SubjectContext.class); + TerminalNode iriRef = mockTerminalNode(""); + when(ctx.IRIREF()).thenReturn(iriRef); + + Resource result = listener.extractSubject(ctx); + assertEquals(mockIRI, result); + verify(mockValueFactory).createIRI("http://example.org/test"); + } + + @Test + @DisplayName("extractSubject should return BNode for BLANK_NODE_LABEL context") + void testExtractSubjectBNode() { + NTriplesParser.SubjectContext ctx = mockRuleContext(NTriplesParser.SubjectContext.class); + TerminalNode bNodeLabel = mockTerminalNode("_:b1"); + when(ctx.BLANK_NODE_LABEL()).thenReturn(bNodeLabel); + + Resource result = listener.extractSubject(ctx); + assertEquals(mockBNode, result); + verify(mockValueFactory).createBNode("b1"); + } + + @Test + @DisplayName("extractPredicate should return IRI for IRIREF context") + void testExtractPredicateIRI() { + NTriplesParser.PredicateContext ctx = mockRuleContext(NTriplesParser.PredicateContext.class); + TerminalNode iriRef = mockTerminalNode(""); + when(ctx.IRIREF()).thenReturn(iriRef); + + IRI result = listener.extractPredicate(ctx); + assertEquals(mockIRI, result); + verify(mockValueFactory).createIRI("http://example.org/test"); + } + + @Test + @DisplayName("extractObject should return IRI for IRIREF context") + void testExtractObjectIRI() { + NTriplesParser.ObjectContext ctx = mockRuleContext(NTriplesParser.ObjectContext.class); + TerminalNode iriRef = mockTerminalNode(""); + when(ctx.IRIREF()).thenReturn(iriRef); + + Value result = listener.extractObject(ctx); + assertEquals(mockIRI, result); + verify(mockValueFactory).createIRI("http://example.org/test"); + } + + @Test + @DisplayName("extractObject should return BNode for BLANK_NODE_LABEL context") + void testExtractObjectBNode() { + NTriplesParser.ObjectContext ctx = mockRuleContext(NTriplesParser.ObjectContext.class); + TerminalNode bNodeLabel = mockTerminalNode("_:b1"); + when(ctx.BLANK_NODE_LABEL()).thenReturn(bNodeLabel); + + Value result = listener.extractObject(ctx); + assertEquals(mockBNode, result); + verify(mockValueFactory).createBNode("b1"); + } + + @Test + @DisplayName("extractObject should return Literal for literal context (simple string)") + void testExtractObjectLiteralSimple() { + NTriplesParser.ObjectContext objCtx = mockRuleContext(NTriplesParser.ObjectContext.class); + NTriplesParser.LiteralContext litCtx = mockRuleContext(NTriplesParser.LiteralContext.class); + TerminalNode stringLiteral = mockTerminalNode("\"test literal\""); + + when(objCtx.literal()).thenReturn(litCtx); + when(litCtx.STRING_LITERAL_QUOTE()).thenReturn(stringLiteral); + when(litCtx.IRIREF()).thenReturn(null); + when(litCtx.LANGTAG()).thenReturn(null); + + Value result = listener.extractObject(objCtx); + assertEquals(mockLiteral, result); + verify(mockValueFactory).createLiteral("test literal"); + } + + @Test + @DisplayName("extractObject should return Literal for literal context (language-tagged)") + void testExtractObjectLiteralLang() { + NTriplesParser.ObjectContext objCtx = mockRuleContext(NTriplesParser.ObjectContext.class); + NTriplesParser.LiteralContext litCtx = mockRuleContext(NTriplesParser.LiteralContext.class); + TerminalNode stringLiteral = mockTerminalNode("\"hello\""); + TerminalNode langTag = mockTerminalNode("@en"); + + when(objCtx.literal()).thenReturn(litCtx); + when(litCtx.STRING_LITERAL_QUOTE()).thenReturn(stringLiteral); + when(litCtx.IRIREF()).thenReturn(null); + when(litCtx.LANGTAG()).thenReturn(langTag); + + Literal expectedLiteral = mock(Literal.class); + when(mockValueFactory.createLiteral("hello", "en")).thenReturn(expectedLiteral); + + Value result = listener.extractObject(objCtx); + assertEquals(expectedLiteral, result); + verify(mockValueFactory).createLiteral("hello", "en"); + } + + + @Test + @DisplayName("unescapeLiteral should throw IllegalArgumentException for invalid \\uXXXX") + void testUnescapeLiteralInvalidUx() throws NoSuchMethodException { + String input = "\"Invalid\\uXXXX\""; + java.lang.reflect.Method method = NTriplesListener.class.getDeclaredMethod("unescapeLiteral", String.class); + method.setAccessible(true); + + + assertThrows(IllegalArgumentException.class, + () -> listener.unescapeLiteral(input), + "Should throw unescapeLiteral should throw IllegalArgumentException for invalid \\uXXXX"); + } + + @Test + @DisplayName("unescapeLiteral should throw IllegalArgumentException for invalid \\UXXXXXXXX") + void testUnescapeLiteralInvalid() throws NoSuchMethodException { + String input = "\"Invalid\\U0000XXX\""; + java.lang.reflect.Method method = NTriplesListener.class.getDeclaredMethod("unescapeLiteral", String.class); + method.setAccessible(true); + + + assertThrows(IllegalArgumentException.class, + () -> listener.unescapeLiteral(input), + "Should throw unescapeLiteral should throw IllegalArgumentException for invalid \\UXXXXXXXX"); + } + + + @Test + @DisplayName("unescapeUri should handle basic escape sequences") + void testUnescapeUriBasicEscapes() throws NoSuchMethodException, java.lang.reflect.InvocationTargetException, IllegalAccessException { + + String input = "http://example.org/path\\>with\\ Date: Wed, 16 Jul 2025 14:29:23 +0200 Subject: [PATCH 08/15] TU parse N-Quads --- .../parser/nquads/ANTLRNQuadsParserTest.java | 195 +++++++++- .../io/parser/nquads/NQuadsListenerTest.java | 360 +++++++++++++++++- 2 files changed, 553 insertions(+), 2 deletions(-) diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParserTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParserTest.java index decc1569e..f596e651f 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParserTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParserTest.java @@ -1,4 +1,197 @@ package fr.inria.corese.core.next.impl.io.parser.nquads; -public class ANTLRNQuadsParserTest { +import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.base.io.RdfFormat; +import fr.inria.corese.core.next.impl.exception.ParsingErrorException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.io.StringReader; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +/** + * Unit tests for the ANTLRNQuadsParser class. + * These tests verify the parser's ability to correctly parse N-Quads + * and interact with the Model and ValueFactory, including error handling + * and unescaping of IRIs and literals, and named graphs. + */ +@ExtendWith(MockitoExtension.class) +class ANTLRNQuadsParserTest { + + @Mock + private Model mockModel; + + @Mock + private ValueFactory mockValueFactory; + + private ANTLRNQuadsParser parser; + + @Mock + private IRI mockSubjectIRI; + @Mock + private IRI mockPredicateIRI; + @Mock + private IRI mockObjectIRI; + @Mock + private IRI mockGraphIRI; + @Mock + private BNode mockSubjectBNode; + @Mock + private BNode mockObjectBNode; + @Mock + private BNode mockGraphBNode; + @Mock + private Literal mockSimpleLiteral; + @Mock + private Literal mockLangLiteral; + @Mock + private Literal mockTypedLiteral; + @Mock + private IRI mockDatatypeIRI; + @Mock + private IRI mockEscapedIRI; + @Mock + private Literal mockEscapedLiteral; + + + @BeforeEach + void setUp() { + parser = new ANTLRNQuadsParser(mockModel, mockValueFactory); + + lenient().when(mockValueFactory.createIRI(anyString())).thenAnswer(invocation -> { + String uri = invocation.getArgument(0); + if (uri.equals("http://example.org/subject")) return mockSubjectIRI; + if (uri.equals("http://example.org/predicate")) return mockPredicateIRI; + if (uri.equals("http://example.org/object")) return mockObjectIRI; + if (uri.equals("http://example.org/graph")) return mockGraphIRI; + if (uri.equals("http://www.w3.org/2001/XMLSchema#integer")) return mockDatatypeIRI; + if (uri.equals("http://example.org/escaped>uri")) return mockEscapedIRI; + if (uri.equals("http://example.org/s ubject")) return mock(IRI.class); + if (uri.equals("http://example.org/path/€")) return mock(IRI.class); + if (uri.equals("http://example.org/path>with\\ { + String label = invocation.getArgument(0); + if (label.equals("sub1")) return mockSubjectBNode; + if (label.equals("obj1")) return mockObjectBNode; + if (label.equals("graph1")) return mockGraphBNode; + return mock(BNode.class); + }); + + lenient().when(mockValueFactory.createLiteral(eq("simple string"))).thenReturn(mockSimpleLiteral); + lenient().when(mockValueFactory.createLiteral(eq("hello"), eq("en"))).thenReturn(mockLangLiteral); + lenient().when(mockValueFactory.createLiteral(eq("123"), any(IRI.class))).thenReturn(mockTypedLiteral); + lenient().when(mockValueFactory.createLiteral(eq("literal with \"quotes\" and \n newline"))).thenReturn(mockEscapedLiteral); + } + + @Test + @DisplayName("Test get RDF format returns NQUADS") + void testGetRDFFormat() { + assertEquals(RdfFormat.NQUADS, parser.getRDFFormat()); + } + + @Test + @DisplayName("Test parsing a basic quad with IRI graph") + void testParseBasicQuadWithIRIGraph() throws ParsingErrorException { + String nquad = " ."; + parser.parse(new StringReader(nquad)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI, mockGraphIRI); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("Test parsing a basic quad with BNode graph") + void testParseBasicQuadWithBNodeGraph() throws ParsingErrorException { + String nquad = " _:graph1 ."; + parser.parse(new StringReader(nquad)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI, mockGraphBNode); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("Test parsing a quad with a literal object and IRI graph") + void testParseQuadWithLiteralObjectAndIRIGraph() throws ParsingErrorException { + String nquad = " \"simple string\" ."; + parser.parse(new StringReader(nquad)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockSimpleLiteral, mockGraphIRI); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("Test parsing a triple (no graph) which should go to default graph") + void testParseTripleToDefaultGraph() throws ParsingErrorException { + String nquad = " ."; + parser.parse(new StringReader(nquad)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI); + verifyNoMoreInteractions(mockModel); + } + + + @Test + @DisplayName("Test parsing a quad with escaped characters in literal") + void testParseEscapedLiteral() throws ParsingErrorException { + String nquad = " \"literal with \\\"quotes\\\" and \\n newline\" ."; + parser.parse(new StringReader(nquad)); + + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockEscapedLiteral, mockGraphIRI); + verifyNoMoreInteractions(mockModel); + } + + + @Test + @DisplayName("Test parsing a quad with Unicode escape in literal (\\uXXXX)") + void testParseUnicodeEscapeLiteralU() throws ParsingErrorException { + String nquad = " \"Hello\\u0020World\" ."; + Literal expectedLiteral = mock(Literal.class); + lenient().when(mockValueFactory.createLiteral(eq("Hello World"))).thenReturn(expectedLiteral); + + parser.parse(new StringReader(nquad)); + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, expectedLiteral, mockGraphIRI); + } + + @Test + @DisplayName("Test parsing a quad with Unicode escape in literal (\\UXXXXXXXX)") + void testParseUnicodeEscapeLiteralUx() throws ParsingErrorException { + String nquad = " \"Euro\" ."; + Literal expectedLiteral = mock(Literal.class); + lenient().when(mockValueFactory.createLiteral(eq("Euro"))).thenReturn(expectedLiteral); + + parser.parse(new StringReader(nquad)); + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, expectedLiteral, mockGraphIRI); + } + + @Test + @DisplayName("Test parsing a quad with Unicode escape in IRI (\\uXXXX) in graph") + void testParseUnicodeEscapeIRIUInGraph() throws ParsingErrorException { + String nquad = " ."; + IRI expectedGraphIRI = mock(IRI.class); + lenient().when(mockValueFactory.createIRI(eq("http://example.org/graphName"))).thenReturn(expectedGraphIRI); + + parser.parse(new StringReader(nquad)); + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI, expectedGraphIRI); + } + + @Test + @DisplayName("Test parsing a quad with Unicode escape in IRI (\\UXXXXXXXX) in graph") + void testParseUnicodeEscapeIRIUxInGraph() throws ParsingErrorException { + String nquad = " ."; + IRI expectedGraphIRI = mock(IRI.class); + lenient().when(mockValueFactory.createIRI(eq("http://example.org/graph"))).thenReturn(expectedGraphIRI); + + parser.parse(new StringReader(nquad)); + verify(mockModel).add(mockSubjectIRI, mockPredicateIRI, mockObjectIRI, expectedGraphIRI); + } } diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListenerTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListenerTest.java index 4ea763faf..9961f68ba 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListenerTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListenerTest.java @@ -1,4 +1,362 @@ package fr.inria.corese.core.next.impl.io.parser.nquads; -public class NQuadsListenerTest { +import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.io.IOOptions; +import fr.inria.corese.core.next.impl.parser.antlr.NQuadsParser; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.tree.TerminalNode; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + +/** + * Unit tests for the NQuadsListener class. + * These tests verify that the listener correctly processes ANTLR parse tree contexts + * to extract and unescape RDF terms (IRIs, Blank Nodes, Literals) and add them to the model, + * including named graphs. + */ +@ExtendWith(MockitoExtension.class) +class NQuadsListenerTest { + + @Mock + private Model mockModel; + + @Mock + private ValueFactory mockValueFactory; + + @Mock + private IOOptions mockIOOptions; + + private NQuadsListener listener; + + + @Mock + private IRI mockIRI; + @Mock + private BNode mockBNode; + @Mock + private Literal mockLiteral; + @Mock + private IRI mockDatatypeIRI; + @Mock + private IRI mockGraphIRI; + @Mock + private BNode mockGraphBNode; + + @BeforeEach + void setUp() { + listener = new NQuadsListener(mockModel, mockValueFactory, mockIOOptions); + + lenient().when(mockValueFactory.createIRI(anyString())).thenAnswer(invocation -> { + String uri = invocation.getArgument(0); + if (uri.equals("http://example.org/test")) return mockIRI; + if (uri.equals("http://example.org/datatype")) return mockDatatypeIRI; + if (uri.equals("http://example.org/graph")) return mockGraphIRI; + if (uri.equals("http://example.org/escaped>uri")) return mock(IRI.class); + if (uri.equals("http://example.org/s ubject")) return mock(IRI.class); + if (uri.equals("http://example.org/path/€")) return mock(IRI.class); + if (uri.equals("http://example.org/path>with\\ { + String label = invocation.getArgument(0); + if (label.equals("b1")) return mockBNode; + if (label.equals("graph1")) return mockGraphBNode; + return mock(BNode.class); + }); + + lenient().when(mockValueFactory.createLiteral(anyString())).thenAnswer(invocation -> { + String value = invocation.getArgument(0); + if (value.equals("test literal")) return mockLiteral; + if (value.equals("literal with \"quotes\" and \n newline")) return mock(Literal.class); + if (value.equals("Hello World")) return mock(Literal.class); + if (value.equals("Euro€")) return mock(Literal.class); + return mock(Literal.class); + }); + lenient().when(mockValueFactory.createLiteral(anyString(), any(IRI.class))).thenReturn(mock(Literal.class)); + lenient().when(mockValueFactory.createLiteral(anyString(), anyString())).thenReturn(mock(Literal.class)); + } + + private TerminalNode mockTerminalNode(String text) { + TerminalNode node = mock(TerminalNode.class); + when(node.getText()).thenReturn(text); + return node; + } + + private T mockRuleContext(Class clazz) { + return mock(clazz); + } + + @Test + @DisplayName("enterStatement and exitStatement should add a quad to the model with IRI graph") + void testEnterExitStatementAddsToModelWithIRIGraph() { + NQuadsParser.StatementContext statementCtx = mockRuleContext(NQuadsParser.StatementContext.class); + NQuadsParser.SubjectContext subjectCtx = mockRuleContext(NQuadsParser.SubjectContext.class); + NQuadsParser.PredicateContext predicateCtx = mockRuleContext(NQuadsParser.PredicateContext.class); + NQuadsParser.ObjectContext objectCtx = mockRuleContext(NQuadsParser.ObjectContext.class); + NQuadsParser.GraphLabelContext graphLabelCtx = mockRuleContext(NQuadsParser.GraphLabelContext.class); + + + TerminalNode subjectIriRef = mockTerminalNode(""); + when(subjectCtx.IRIREF()).thenReturn(subjectIriRef); + + TerminalNode predicateIriRef = mockTerminalNode(""); + when(predicateCtx.IRIREF()).thenReturn(predicateIriRef); + + TerminalNode objectIriRef = mockTerminalNode(""); + when(objectCtx.IRIREF()).thenReturn(objectIriRef); + + TerminalNode graphIriRef = mockTerminalNode(""); + when(graphLabelCtx.IRIREF()).thenReturn(graphIriRef); + + + when(statementCtx.subject()).thenReturn(subjectCtx); + when(statementCtx.predicate()).thenReturn(predicateCtx); + when(statementCtx.object()).thenReturn(objectCtx); + when(statementCtx.graphLabel()).thenReturn(graphLabelCtx); + + IRI actualSubjectIRI = mock(IRI.class); + IRI actualPredicateIRI = mock(IRI.class); + IRI actualObjectIRI = mock(IRI.class); + IRI actualGraphIRI = mock(IRI.class); + when(mockValueFactory.createIRI("http://example.org/subject")).thenReturn(actualSubjectIRI); + when(mockValueFactory.createIRI("http://example.org/predicate")).thenReturn(actualPredicateIRI); + when(mockValueFactory.createIRI("http://example.org/object")).thenReturn(actualObjectIRI); + when(mockValueFactory.createIRI("http://example.org/graph")).thenReturn(actualGraphIRI); + + + listener.enterStatement(statementCtx); + listener.exitStatement(statementCtx); + + verify(mockModel).add(actualSubjectIRI, actualPredicateIRI, actualObjectIRI, actualGraphIRI); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("enterStatement and exitStatement should add a triple to the model (default graph)") + void testEnterExitStatementAddsToModelDefaultGraph() { + NQuadsParser.StatementContext statementCtx = mockRuleContext(NQuadsParser.StatementContext.class); + NQuadsParser.SubjectContext subjectCtx = mockRuleContext(NQuadsParser.SubjectContext.class); + NQuadsParser.PredicateContext predicateCtx = mockRuleContext(NQuadsParser.PredicateContext.class); + NQuadsParser.ObjectContext objectCtx = mockRuleContext(NQuadsParser.ObjectContext.class); + + TerminalNode subjectIriRef = mockTerminalNode(""); + when(subjectCtx.IRIREF()).thenReturn(subjectIriRef); + + TerminalNode predicateIriRef = mockTerminalNode(""); + when(predicateCtx.IRIREF()).thenReturn(predicateIriRef); + + TerminalNode objectIriRef = mockTerminalNode(""); + when(objectCtx.IRIREF()).thenReturn(objectIriRef); + + when(statementCtx.subject()).thenReturn(subjectCtx); + when(statementCtx.predicate()).thenReturn(predicateCtx); + when(statementCtx.object()).thenReturn(objectCtx); + when(statementCtx.graphLabel()).thenReturn(null); + + IRI actualSubjectIRI = mock(IRI.class); + IRI actualPredicateIRI = mock(IRI.class); + IRI actualObjectIRI = mock(IRI.class); + when(mockValueFactory.createIRI("http://example.org/subject")).thenReturn(actualSubjectIRI); + when(mockValueFactory.createIRI("http://example.org/predicate")).thenReturn(actualPredicateIRI); + when(mockValueFactory.createIRI("http://example.org/object")).thenReturn(actualObjectIRI); + + + listener.enterStatement(statementCtx); + listener.exitStatement(statementCtx); + + verify(mockModel).add(actualSubjectIRI, actualPredicateIRI, actualObjectIRI); + verifyNoMoreInteractions(mockModel); + } + + @Test + @DisplayName("extractSubject should return IRI for IRIREF context") + void testExtractSubjectIRI() { + NQuadsParser.SubjectContext ctx = mockRuleContext(NQuadsParser.SubjectContext.class); + TerminalNode iriRef = mockTerminalNode(""); + when(ctx.IRIREF()).thenReturn(iriRef); + + Resource result = listener.extractSubject(ctx); + assertEquals(mockIRI, result); + verify(mockValueFactory).createIRI("http://example.org/test"); + } + + @Test + @DisplayName("extractSubject should return BNode for BLANK_NODE_LABEL context") + void testExtractSubjectBNode() { + NQuadsParser.SubjectContext ctx = mockRuleContext(NQuadsParser.SubjectContext.class); + TerminalNode bNodeLabel = mockTerminalNode("_:b1"); + when(ctx.BLANK_NODE_LABEL()).thenReturn(bNodeLabel); + + Resource result = listener.extractSubject(ctx); + assertEquals(mockBNode, result); + verify(mockValueFactory).createBNode("b1"); + } + + @Test + @DisplayName("extractPredicate should return IRI for IRIREF context") + void testExtractPredicateIRI() { + NQuadsParser.PredicateContext ctx = mockRuleContext(NQuadsParser.PredicateContext.class); + TerminalNode iriRef = mockTerminalNode(""); + when(ctx.IRIREF()).thenReturn(iriRef); + + IRI result = listener.extractPredicate(ctx); + assertEquals(mockIRI, result); + verify(mockValueFactory).createIRI("http://example.org/test"); + } + + @Test + @DisplayName("extractObject should return IRI for IRIREF context") + void testExtractObjectIRI() { + NQuadsParser.ObjectContext ctx = mockRuleContext(NQuadsParser.ObjectContext.class); + TerminalNode iriRef = mockTerminalNode(""); + when(ctx.IRIREF()).thenReturn(iriRef); + + Value result = listener.extractObject(ctx); + assertEquals(mockIRI, result); + verify(mockValueFactory).createIRI("http://example.org/test"); + } + + @Test + @DisplayName("extractObject should return BNode for BLANK_NODE_LABEL context") + void testExtractObjectBNode() { + NQuadsParser.ObjectContext ctx = mockRuleContext(NQuadsParser.ObjectContext.class); + TerminalNode bNodeLabel = mockTerminalNode("_:b1"); + when(ctx.BLANK_NODE_LABEL()).thenReturn(bNodeLabel); + + Value result = listener.extractObject(ctx); + assertEquals(mockBNode, result); + verify(mockValueFactory).createBNode("b1"); + } + + @Test + @DisplayName("extractObject should return Literal for literal context (simple string)") + void testExtractObjectLiteralSimple() { + NQuadsParser.ObjectContext objCtx = mockRuleContext(NQuadsParser.ObjectContext.class); + NQuadsParser.LiteralContext litCtx = mockRuleContext(NQuadsParser.LiteralContext.class); + TerminalNode stringLiteral = mockTerminalNode("\"test literal\""); + + when(objCtx.literal()).thenReturn(litCtx); + when(litCtx.STRING_LITERAL_QUOTE()).thenReturn(stringLiteral); + when(litCtx.IRIREF()).thenReturn(null); + when(litCtx.LANGTAG()).thenReturn(null); + + Value result = listener.extractObject(objCtx); + assertEquals(mockLiteral, result); + verify(mockValueFactory).createLiteral("test literal"); + } + + @Test + @DisplayName("extractObject should return Literal for literal context (language-tagged)") + void testExtractObjectLiteralLang() { + NQuadsParser.ObjectContext objCtx = mockRuleContext(NQuadsParser.ObjectContext.class); + NQuadsParser.LiteralContext litCtx = mockRuleContext(NQuadsParser.LiteralContext.class); + TerminalNode stringLiteral = mockTerminalNode("\"hello\""); + TerminalNode langTag = mockTerminalNode("@en"); + + when(objCtx.literal()).thenReturn(litCtx); + when(litCtx.STRING_LITERAL_QUOTE()).thenReturn(stringLiteral); + when(litCtx.IRIREF()).thenReturn(null); + when(litCtx.LANGTAG()).thenReturn(langTag); + + Literal expectedLiteral = mock(Literal.class); + when(mockValueFactory.createLiteral("hello", "en")).thenReturn(expectedLiteral); + + Value result = listener.extractObject(objCtx); + assertEquals(expectedLiteral, result); + verify(mockValueFactory).createLiteral("hello", "en"); + } + + + @Test + @DisplayName("extractGraph should return IRI for IRIREF context") + void testExtractGraphIRI() { + NQuadsParser.GraphLabelContext ctx = mockRuleContext(NQuadsParser.GraphLabelContext.class); + TerminalNode iriRef = mockTerminalNode(""); + when(ctx.IRIREF()).thenReturn(iriRef); + + Resource result = listener.extractGraph(ctx); + assertEquals(mockGraphIRI, result); + verify(mockValueFactory).createIRI("http://example.org/graph"); + } + + @Test + @DisplayName("extractGraph should return BNode for BLANK_NODE_LABEL context") + void testExtractGraphBNode() { + NQuadsParser.GraphLabelContext ctx = mockRuleContext(NQuadsParser.GraphLabelContext.class); + TerminalNode bNodeLabel = mockTerminalNode("_:graph1"); + when(ctx.BLANK_NODE_LABEL()).thenReturn(bNodeLabel); + + Resource result = listener.extractGraph(ctx); + assertEquals(mockGraphBNode, result); + verify(mockValueFactory).createBNode("graph1"); + } + + + + + @Test + @DisplayName("unescapeLiteral should throw IllegalArgumentException for invalid \\UXXXXXXXX") + void testUnescapeLiteralInvalidUx() throws NoSuchMethodException { + String input = "\"Invalid\\U0000XXX\""; + java.lang.reflect.Method method = NQuadsListener.class.getDeclaredMethod("unescapeLiteral", String.class); + method.setAccessible(true); + assertThrows(IllegalArgumentException.class, + () -> listener.unescapeLiteral(input), + "Should throw for malformed \\UXXXXXXXX escape sequence"); + } + + + @Test + @DisplayName("unescapeUri should handle basic escape sequences") + void testUnescapeUriBasicEscapes() throws NoSuchMethodException, java.lang.reflect.InvocationTargetException, IllegalAccessException { + + String input = "http://example.org/path\\>with\\ listener.unescapeLiteral(input), + "Should throw unescapeUri should throw IllegalArgumentException for invalid \\uXXXX"); + + } + + } From bb0e9df241c78c0efb8fbf51b14dd5253c109c51 Mon Sep 17 00:00:00 2001 From: "AD\\aabdoun" Date: Wed, 16 Jul 2025 14:43:48 +0200 Subject: [PATCH 09/15] correction le Warning: Output directory "C:\DEV\corese-core\build\generated\javacc\sparqlCorese\fr\inria\corese\core\sparql\triple\javacc1" does not exist. Creating the directory. --- build.gradle.kts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 80b394731..632e7d3fa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -59,6 +59,14 @@ sourceSets { } } +tasks.register("createJavaCCDir") { + doLast { + mkdir("${layout.buildDirectory.get()}/generated/javacc/sparqlCorese/fr/inria/corese/core/sparql/triple/javacc1") + } +} +tasks.named("javaccSparqlCorese") { + dependsOn("createJavaCCDir") +} // Ensure JavaCC generation happens before compilation tasks.named("compileJava") { dependsOn("javaccSparqlCorese") From 9270058a48316f856d325205d4fc46e1d1d4f371 Mon Sep 17 00:00:00 2001 From: "AD\\aabdoun" Date: Wed, 16 Jul 2025 14:54:49 +0200 Subject: [PATCH 10/15] =?UTF-8?q?adapte=20=20apr=C3=A8s=20avoir=20effectu?= =?UTF-8?q?=C3=A9=20le=20rebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/impl/io/parser/ParserFactory.java | 8 ++++---- .../io/parser/nquads/ANTLRNQuadsParser.java | 6 +++--- .../parser/ntriples/ANTLRNTriplesParser.java | 6 +++--- .../next/impl/io/parser/ParserFactoryTest.java | 18 +++++++++--------- .../parser/nquads/ANTLRNQuadsParserTest.java | 4 ++-- .../ntriples/ANTLRNTriplesParserTest.java | 4 ++-- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ParserFactory.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ParserFactory.java index dce59a80b..8b43d930a 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ParserFactory.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ParserFactory.java @@ -40,9 +40,9 @@ public RDFParser createRDFParser(RDFFormat format, Model model, ValueFactory fac return new JSONLDParser(model, factory, config); } else if (format == RDFFormat.TURTLE) { return new ANTLRTurtleParser(model, factory, config); - } else if (format == RdfFormat.NTRIPLES) { + } else if (format == RDFFormat.NTRIPLES) { return new ANTLRNTriplesParser(model, factory, config); - } else if (format == RdfFormat.NQUADS) { + } else if (format == RDFFormat.NQUADS) { return new ANTLRNQuadsParser(model, factory, config); } throw new IllegalArgumentException("Unsupported format: " + format); @@ -61,9 +61,9 @@ public RDFParser createRDFParser(RDFFormat format, Model model, ValueFactory fac return new JSONLDParser(model, factory); } else if (format == RDFFormat.TURTLE) { return new ANTLRTurtleParser(model, factory); - } else if (format == RdfFormat.NTRIPLES) { + } else if (format == RDFFormat.NTRIPLES) { return new ANTLRNTriplesParser(model, factory); - } else if (format == RdfFormat.NQUADS) { + } else if (format == RDFFormat.NQUADS) { return new ANTLRNQuadsParser(model, factory); } throw new IllegalArgumentException("Unsupported format: " + format); diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParser.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParser.java index 8831352fe..014a33984 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParser.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParser.java @@ -2,7 +2,7 @@ import fr.inria.corese.core.next.api.Model; 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.base.io.RDFFormat; import fr.inria.corese.core.next.api.base.io.parser.AbstractRDFParser; import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.impl.exception.ParsingErrorException; @@ -37,8 +37,8 @@ public ANTLRNQuadsParser(Model model, ValueFactory factory, IOOptions config) { } @Override - public RdfFormat getRDFFormat() { - return RdfFormat.NQUADS; + public RDFFormat getRDFFormat() { + return RDFFormat.NQUADS; } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java index 4430491d1..279426841 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java @@ -2,7 +2,7 @@ import fr.inria.corese.core.next.api.Model; 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.base.io.RDFFormat; import fr.inria.corese.core.next.api.base.io.parser.AbstractRDFParser; import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.impl.exception.ParsingErrorException; @@ -37,8 +37,8 @@ public ANTLRNTriplesParser(Model model, ValueFactory factory, IOOptions config) } @Override - public RdfFormat getRDFFormat() { - return RdfFormat.NTRIPLES; + public RDFFormat getRDFFormat() { + return RDFFormat.NTRIPLES; } diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ParserFactoryTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ParserFactoryTest.java index 66dce0f32..eff44aa9b 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ParserFactoryTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ParserFactoryTest.java @@ -2,7 +2,7 @@ import fr.inria.corese.core.next.api.Model; 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.base.io.RDFFormat; import fr.inria.corese.core.next.api.io.parser.RDFParser; import fr.inria.corese.core.next.api.io.parser.RDFParserOptions; import fr.inria.corese.core.next.impl.io.parser.jsonld.JSONLDParser; @@ -47,7 +47,7 @@ void setUp() { @Test @DisplayName("createRDFParser (with config) should return JSONLDParser for JSONLD format") void testCreateRDFParserWithConfig_JSONLD() { - RDFParser parser = parserFactory.createRDFParser(RdfFormat.JSONLD, mockModel, mockValueFactory, mockParserOptions); + RDFParser parser = parserFactory.createRDFParser(RDFFormat.JSONLD, mockModel, mockValueFactory, mockParserOptions); assertNotNull(parser); assertTrue(parser instanceof JSONLDParser); } @@ -55,7 +55,7 @@ void testCreateRDFParserWithConfig_JSONLD() { @Test @DisplayName("createRDFParser (with config) should return ANTLRTurtleParser for TURTLE format") void testCreateRDFParserWithConfig_TURTLE() { - RDFParser parser = parserFactory.createRDFParser(RdfFormat.TURTLE, mockModel, mockValueFactory, mockParserOptions); + RDFParser parser = parserFactory.createRDFParser(RDFFormat.TURTLE, mockModel, mockValueFactory, mockParserOptions); assertNotNull(parser); assertTrue(parser instanceof ANTLRTurtleParser); } @@ -63,7 +63,7 @@ void testCreateRDFParserWithConfig_TURTLE() { @Test @DisplayName("createRDFParser (with config) should return ANTLRNTriplesParser for N-TRIPLES format") void testCreateRDFParserWithConfig_NTRIPLES() { - RDFParser parser = parserFactory.createRDFParser(RdfFormat.NTRIPLES, mockModel, mockValueFactory, mockParserOptions); + RDFParser parser = parserFactory.createRDFParser(RDFFormat.NTRIPLES, mockModel, mockValueFactory, mockParserOptions); assertNotNull(parser); assertTrue(parser instanceof ANTLRNTriplesParser); } @@ -71,7 +71,7 @@ void testCreateRDFParserWithConfig_NTRIPLES() { @Test @DisplayName("createRDFParser (with config) should return ANTLRNQuadsParser for N-QUADS format") void testCreateRDFParserWithConfig_NQUADS() { - RDFParser parser = parserFactory.createRDFParser(RdfFormat.NQUADS, mockModel, mockValueFactory, mockParserOptions); + RDFParser parser = parserFactory.createRDFParser(RDFFormat.NQUADS, mockModel, mockValueFactory, mockParserOptions); assertNotNull(parser); assertTrue(parser instanceof ANTLRNQuadsParser); } @@ -80,7 +80,7 @@ void testCreateRDFParserWithConfig_NQUADS() { @Test @DisplayName("createRDFParser (without config) should return JSONLDParser for JSONLD format") void testCreateRDFParserWithoutConfig_JSONLD() { - RDFParser parser = parserFactory.createRDFParser(RdfFormat.JSONLD, mockModel, mockValueFactory); + RDFParser parser = parserFactory.createRDFParser(RDFFormat.JSONLD, mockModel, mockValueFactory); assertNotNull(parser); assertTrue(parser instanceof JSONLDParser); } @@ -88,7 +88,7 @@ void testCreateRDFParserWithoutConfig_JSONLD() { @Test @DisplayName("createRDFParser (without config) should return ANTLRTurtleParser for TURTLE format") void testCreateRDFParserWithoutConfig_TURTLE() { - RDFParser parser = parserFactory.createRDFParser(RdfFormat.TURTLE, mockModel, mockValueFactory); + RDFParser parser = parserFactory.createRDFParser(RDFFormat.TURTLE, mockModel, mockValueFactory); assertNotNull(parser); assertTrue(parser instanceof ANTLRTurtleParser); } @@ -96,7 +96,7 @@ void testCreateRDFParserWithoutConfig_TURTLE() { @Test @DisplayName("createRDFParser (without config) should return ANTLRNTriplesParser for N-TRIPLES format") void testCreateRDFParserWithoutConfig_NTRIPLES() { - RDFParser parser = parserFactory.createRDFParser(RdfFormat.NTRIPLES, mockModel, mockValueFactory); + RDFParser parser = parserFactory.createRDFParser(RDFFormat.NTRIPLES, mockModel, mockValueFactory); assertNotNull(parser); assertTrue(parser instanceof ANTLRNTriplesParser); } @@ -104,7 +104,7 @@ void testCreateRDFParserWithoutConfig_NTRIPLES() { @Test @DisplayName("createRDFParser (without config) should return ANTLRNQuadsParser for N-QUADS format") void testCreateRDFParserWithoutConfig_NQUADS() { - RDFParser parser = parserFactory.createRDFParser(RdfFormat.NQUADS, mockModel, mockValueFactory); + RDFParser parser = parserFactory.createRDFParser(RDFFormat.NQUADS, mockModel, mockValueFactory); assertNotNull(parser); assertTrue(parser instanceof ANTLRNQuadsParser); } diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParserTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParserTest.java index f596e651f..e0a26f63e 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParserTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParserTest.java @@ -1,7 +1,7 @@ package fr.inria.corese.core.next.impl.io.parser.nquads; import fr.inria.corese.core.next.api.*; -import fr.inria.corese.core.next.api.base.io.RdfFormat; +import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.impl.exception.ParsingErrorException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -96,7 +96,7 @@ void setUp() { @Test @DisplayName("Test get RDF format returns NQUADS") void testGetRDFFormat() { - assertEquals(RdfFormat.NQUADS, parser.getRDFFormat()); + assertEquals(RDFFormat.NQUADS, parser.getRDFFormat()); } @Test diff --git a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParserTest.java b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParserTest.java index 083ec4c23..16c6ce6ea 100644 --- a/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParserTest.java +++ b/src/test/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParserTest.java @@ -1,7 +1,7 @@ package fr.inria.corese.core.next.impl.io.parser.ntriples; import fr.inria.corese.core.next.api.*; -import fr.inria.corese.core.next.api.base.io.RdfFormat; +import fr.inria.corese.core.next.api.base.io.RDFFormat; import fr.inria.corese.core.next.impl.exception.ParsingErrorException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -86,7 +86,7 @@ void setUp() { @Test @DisplayName("Test get RDF format returns NTRIPLES") void testGetRDFFormat() { - assertEquals(RdfFormat.NTRIPLES, parser.getRDFFormat()); + assertEquals(RDFFormat.NTRIPLES, parser.getRDFFormat()); } @Test From 8bee90032d1685cd3ef0ff6edb377e9d8f84d107 Mon Sep 17 00:00:00 2001 From: "AD\\aabdoun" Date: Tue, 22 Jul 2025 09:09:42 +0200 Subject: [PATCH 11/15] correction le Warning: Output directory "C:\DEV\corese-core\build\generated\javacc\sparqlCorese\fr\inria\corese\core\sparql\triple\javacc1" does not exist. Creating the directory. --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 632e7d3fa..a85a52aca 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -61,7 +61,7 @@ sourceSets { tasks.register("createJavaCCDir") { doLast { - mkdir("${layout.buildDirectory.get()}/generated/javacc/sparqlCorese/fr/inria/corese/core/sparql/triple/javacc1") + mkdir("${layout.buildDirectory.get()}/generated-src/javacc/fr/inria/corese/core/sparql/triple/javacc1") } } tasks.named("javaccSparqlCorese") { From 41c3def326681859bf405bfe9e10d48739335d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20C=C3=A9r=C3=A8s?= Date: Tue, 22 Jul 2025 09:50:19 +0200 Subject: [PATCH 12/15] Refactor JavaCC and Antlr task dependencies and output directories --- build.gradle.kts | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a85a52aca..b8e91e7e1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -59,14 +59,6 @@ sourceSets { } } -tasks.register("createJavaCCDir") { - doLast { - mkdir("${layout.buildDirectory.get()}/generated-src/javacc/fr/inria/corese/core/sparql/triple/javacc1") - } -} -tasks.named("javaccSparqlCorese") { - dependsOn("createJavaCCDir") -} // Ensure JavaCC generation happens before compilation tasks.named("compileJava") { dependsOn("javaccSparqlCorese") @@ -357,8 +349,9 @@ tasks.withType().configureEach { // Configure the Antlr task to generate parser code with specific arguments tasks.named("generateGrammarSource") { arguments.addAll(listOf("-visitor", "-long-messages", "-package", "fr.inria.corese.core.next.impl.parser.antlr")) - outputDirectory = layout.buildDirectory.dir("generated-src/antlr/main/fr/inria/corese/core/next/impl/parser/antlr").get().asFile - outputs.dirs(outputDirectory) + outputDirectory = antlrPackageDir + inputs.files(fileTree("src/main/antlr")) + outputs.dir(antlrPackageDir) } // Ensure Java compilation depends on both JavaCC and Antlr code generation From 062d3a73da596cc2f7e5839f62cccf5325156c3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20C=C3=A9r=C3=A8s?= Date: Tue, 22 Jul 2025 09:51:00 +0200 Subject: [PATCH 13/15] Add constructors with configuration options for ANTLRNQuadsParser and ANTLRNTriplesParser; update NQuadsListener and NTriplesListener constructors --- .../impl/io/parser/nquads/ANTLRNQuadsParser.java | 13 +++++++++++++ .../next/impl/io/parser/nquads/NQuadsListener.java | 7 +++++++ .../io/parser/ntriples/ANTLRNTriplesParser.java | 13 +++++++++++++ .../impl/io/parser/ntriples/NTriplesListener.java | 7 +++++++ 4 files changed, 40 insertions(+) diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParser.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParser.java index 014a33984..742f2aa27 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParser.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/ANTLRNQuadsParser.java @@ -28,10 +28,23 @@ */ public class ANTLRNQuadsParser extends AbstractRDFParser { + /** + * Constructor for the ANTLRNQuadsParser. + * + * @param model The RDF model to populate. + * @param factory The ValueFactory for creating RDF resources. + */ public ANTLRNQuadsParser(Model model, ValueFactory factory) { super(model, factory); } + /** + * Constructor for the ANTLRNQuadsParser with configuration options. + * + * @param model The RDF model to populate. + * @param factory The ValueFactory for creating RDF resources. + * @param config The configuration options for parsing. + */ public ANTLRNQuadsParser(Model model, ValueFactory factory, IOOptions config) { super(model, factory, config); } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java index db55935c9..9259402f8 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java @@ -24,6 +24,13 @@ public class NQuadsListener extends NQuadsBaseListener { private IRI currentPredicate; private Resource currentGraph; + /** + * Constructor for the NQuadsListener. + * + * @param model The RDF model to populate. + * @param factory The ValueFactory for creating RDF resources. + * @param options IOOptions for configuration (if any). + */ public NQuadsListener(Model model, ValueFactory factory, IOOptions options) { this.model = model; this.factory = factory; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java index 279426841..75370cf8f 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/ANTLRNTriplesParser.java @@ -28,10 +28,23 @@ */ public class ANTLRNTriplesParser extends AbstractRDFParser { + /** + * Constructor for the ANTLRNTriplesParser. + * + * @param model The RDF model to populate. + * @param factory The ValueFactory for creating RDF resources. + */ public ANTLRNTriplesParser(Model model, ValueFactory factory) { super(model, factory); } + /** + * Constructor for the ANTLRNTriplesParser with configuration options. + * + * @param model The RDF model to populate. + * @param factory The ValueFactory for creating RDF resources. + * @param config The configuration options for parsing. + */ public ANTLRNTriplesParser(Model model, ValueFactory factory, IOOptions config) { super(model, factory, config); } diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListener.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListener.java index 040dbb6d6..f6cb6852a 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListener.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListener.java @@ -23,6 +23,13 @@ public class NTriplesListener extends NTriplesBaseListener { private Resource currentSubject; private IRI currentPredicate; + /** + * Constructor for the NTriplesListener. + * + * @param model The RDF model to populate. + * @param factory The ValueFactory for creating RDF resources. + * @param options IOOptions for configuration (if any). + */ public NTriplesListener(Model model, ValueFactory factory, IOOptions options) { this.model = model; this.factory = factory; From 20709b2ead438aa186f85ab83255a8c474f4b2b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20C=C3=A9r=C3=A8s?= Date: Tue, 22 Jul 2025 10:10:55 +0200 Subject: [PATCH 14/15] Refactor import statements in NQuadsListener and NTriplesListener for clarity; remove unused logger initialization --- .../next/impl/io/parser/nquads/NQuadsListener.java | 12 +++++++----- .../impl/io/parser/ntriples/NTriplesListener.java | 12 +++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java index 9259402f8..6280d47a8 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/nquads/NQuadsListener.java @@ -1,11 +1,14 @@ package fr.inria.corese.core.next.impl.io.parser.nquads; -import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.IRI; +import fr.inria.corese.core.next.api.Literal; +import fr.inria.corese.core.next.api.Model; +import fr.inria.corese.core.next.api.Resource; +import fr.inria.corese.core.next.api.Value; +import fr.inria.corese.core.next.api.ValueFactory; import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.impl.parser.antlr.NQuadsBaseListener; import fr.inria.corese.core.next.impl.parser.antlr.NQuadsParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Listener for the ANTLR4 generated parser for N-Quads. @@ -14,10 +17,9 @@ */ public class NQuadsListener extends NQuadsBaseListener { - private static final Logger logger = LoggerFactory.getLogger(NQuadsListener.class); - private final Model model; private final ValueFactory factory; + @SuppressWarnings("unused") private final IOOptions options; private Resource currentSubject; diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListener.java b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListener.java index f6cb6852a..898ca2053 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListener.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/parser/ntriples/NTriplesListener.java @@ -1,11 +1,14 @@ package fr.inria.corese.core.next.impl.io.parser.ntriples; -import fr.inria.corese.core.next.api.*; +import fr.inria.corese.core.next.api.IRI; +import fr.inria.corese.core.next.api.Literal; +import fr.inria.corese.core.next.api.Model; +import fr.inria.corese.core.next.api.Resource; +import fr.inria.corese.core.next.api.Value; +import fr.inria.corese.core.next.api.ValueFactory; import fr.inria.corese.core.next.api.io.IOOptions; import fr.inria.corese.core.next.impl.parser.antlr.NTriplesBaseListener; import fr.inria.corese.core.next.impl.parser.antlr.NTriplesParser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Listener for the ANTLR4 generated parser for N-Triples. @@ -14,10 +17,9 @@ */ public class NTriplesListener extends NTriplesBaseListener { - private static final Logger logger = LoggerFactory.getLogger(NTriplesListener.class); - private final Model model; private final ValueFactory factory; + @SuppressWarnings("unused") private final IOOptions options; private Resource currentSubject; From f92fa739762f991c6ec5befdcc6bede2e88dd500 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20C=C3=A9r=C3=A8s?= Date: Tue, 22 Jul 2025 10:11:02 +0200 Subject: [PATCH 15/15] Refactor import statements across multiple serializers for improved organization and clarity --- .../base/model/literal/AbstractDuration.java | 12 ++++-- .../base/AbstractGraphSerializer.java | 42 ++++++++++++++----- .../base/AbstractLineBasedSerializer.java | 26 +++++++----- ...itaniumRDFDatasetSerializationAdapter.java | 42 +++++++++++++------ .../serialization/rdfxml/XmlSerializer.java | 34 ++++++++++----- .../io/serialization/trig/TriGSerializer.java | 14 +++++-- .../turtle/TurtleSerializer.java | 16 +++---- 7 files changed, 129 insertions(+), 57 deletions(-) diff --git a/src/main/java/fr/inria/corese/core/next/api/base/model/literal/AbstractDuration.java b/src/main/java/fr/inria/corese/core/next/api/base/model/literal/AbstractDuration.java index 3628af23f..f5475f737 100644 --- a/src/main/java/fr/inria/corese/core/next/api/base/model/literal/AbstractDuration.java +++ b/src/main/java/fr/inria/corese/core/next/api/base/model/literal/AbstractDuration.java @@ -1,12 +1,16 @@ package fr.inria.corese.core.next.api.base.model.literal; -import fr.inria.corese.core.next.api.literal.CoreDatatype; -import fr.inria.corese.core.next.impl.common.literal.XSD; - import java.time.DateTimeException; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalUnit; -import java.util.*; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import fr.inria.corese.core.next.api.literal.CoreDatatype; +import fr.inria.corese.core.next.impl.common.literal.XSD; /** * Abstract class representing a duration literal in RDF. 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 f031da378..fef457855 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,23 +1,45 @@ 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.RDFSerializer; -import fr.inria.corese.core.next.impl.common.literal.RDF; -import fr.inria.corese.core.next.impl.io.serialization.option.*; -import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; -import fr.inria.corese.core.next.impl.exception.SerializationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.BufferedWriter; import java.io.IOException; import java.io.UncheckedIOException; import java.io.Writer; import java.net.URI; import java.net.URISyntaxException; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import fr.inria.corese.core.next.api.IRI; +import fr.inria.corese.core.next.api.Literal; +import fr.inria.corese.core.next.api.Model; +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.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.AbstractTFamilyOption; +import fr.inria.corese.core.next.impl.io.serialization.option.BlankNodeStyleEnum; +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; + /** * Abstract base class for RDF serializers based on TriG and Turtle syntax. * This class contains the common logic for serializing RDF models 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 5f3ae0be4..f0af2a595 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 @@ -1,15 +1,5 @@ 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.RDFSerializer; -import fr.inria.corese.core.next.impl.common.literal.RDF; -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; -import fr.inria.corese.core.next.impl.exception.SerializationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.BufferedWriter; import java.io.IOException; import java.io.UncheckedIOException; @@ -18,6 +8,22 @@ import java.util.Objects; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import fr.inria.corese.core.next.api.IRI; +import fr.inria.corese.core.next.api.Literal; +import fr.inria.corese.core.next.api.Model; +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.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; + /** * Base class for line-based RDF serializers (N-Triples, N-Quads). * Contains all the common logic for writing statements line by line. diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/jsonld/TitaniumRDFDatasetSerializationAdapter.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/jsonld/TitaniumRDFDatasetSerializationAdapter.java index c2b33d39a..d02fce12a 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/jsonld/TitaniumRDFDatasetSerializationAdapter.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/jsonld/TitaniumRDFDatasetSerializationAdapter.java @@ -1,25 +1,43 @@ package fr.inria.corese.core.next.impl.io.serialization.jsonld; -import com.apicatalog.rdf.*; -import fr.inria.corese.core.next.api.*; -import fr.inria.corese.core.next.api.literal.CoreDatatype; -import fr.inria.corese.core.next.impl.common.util.IRIUtils; -import fr.inria.corese.core.next.impl.common.vocabulary.RDF; -import fr.inria.corese.core.next.impl.common.vocabulary.XSD; -import fr.inria.corese.core.next.impl.exception.SerializationException; +import static fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants.DEFAULT_GRAPH_IRI; -import javax.xml.datatype.DatatypeConfigurationException; -import javax.xml.datatype.DatatypeFactory; -import javax.xml.datatype.XMLGregorianCalendar; import java.math.BigDecimal; import java.math.BigInteger; import java.time.Duration; import java.time.LocalDateTime; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAmount; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; -import static fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants.DEFAULT_GRAPH_IRI; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; + +import com.apicatalog.rdf.RdfDataset; +import com.apicatalog.rdf.RdfGraph; +import com.apicatalog.rdf.RdfLiteral; +import com.apicatalog.rdf.RdfNQuad; +import com.apicatalog.rdf.RdfResource; +import com.apicatalog.rdf.RdfTriple; +import com.apicatalog.rdf.RdfValue; + +import fr.inria.corese.core.next.api.BNode; +import fr.inria.corese.core.next.api.IRI; +import fr.inria.corese.core.next.api.Literal; +import fr.inria.corese.core.next.api.Model; +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.literal.CoreDatatype; +import fr.inria.corese.core.next.impl.common.util.IRIUtils; +import fr.inria.corese.core.next.impl.common.vocabulary.RDF; +import fr.inria.corese.core.next.impl.common.vocabulary.XSD; +import fr.inria.corese.core.next.impl.exception.SerializationException; /** * Adapter class from Model to RdfDataset for usage in the JSON-LD serialization process using the titanium library. diff --git a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/XmlSerializer.java b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/XmlSerializer.java index e61d61aaa..4c59eaff7 100644 --- a/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/XmlSerializer.java +++ b/src/main/java/fr/inria/corese/core/next/impl/io/serialization/rdfxml/XmlSerializer.java @@ -1,21 +1,35 @@ package fr.inria.corese.core.next.impl.io.serialization.rdfxml; -import fr.inria.corese.core.next.api.*; -import fr.inria.corese.core.next.api.io.serialization.RDFSerializer; -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; -import fr.inria.corese.core.next.impl.exception.SerializationException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.BufferedWriter; import java.io.IOException; import java.io.UncheckedIOException; import java.io.Writer; -import java.util.*; +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.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import fr.inria.corese.core.next.api.IRI; +import fr.inria.corese.core.next.api.Literal; +import fr.inria.corese.core.next.api.Model; +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; +import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; + /** * Serializes a {@link Model} to RDF/XML format. * This class provides a method to write the statements of a model to a {@link Writer} 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 4fe2d9605..3e6a3db90 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 @@ -1,5 +1,15 @@ package fr.inria.corese.core.next.impl.io.serialization.trig; +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; + import fr.inria.corese.core.next.api.IRI; import fr.inria.corese.core.next.api.Model; import fr.inria.corese.core.next.api.Resource; @@ -7,10 +17,6 @@ import fr.inria.corese.core.next.impl.io.serialization.base.AbstractGraphSerializer; import fr.inria.corese.core.next.impl.io.serialization.util.SerializationConstants; -import java.io.IOException; -import java.io.Writer; -import java.util.*; - /** * Serializes a {@link Model} to TriG format with comprehensive syntax support. * This class provides a method to write the declarations of a model to a {@link Writer} 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 0d7fe4918..326725de9 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 @@ -1,16 +1,18 @@ package fr.inria.corese.core.next.impl.io.serialization.turtle; -import fr.inria.corese.core.next.api.*; -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.util.SerializationConstants; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.Writer; import java.util.Objects; +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.util.SerializationConstants; + /** * Serializes a {@link Model} to Turtle format with comprehensive syntax support. * This class provides a method to write the declarations of a model to a {@link Writer}