Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ or use the default native backend (`memory`).

**- [Storage Backend Plugin Guide](docs/STORAGE_PLUGIN_GUIDE.md)**


## Contributing

We welcome contributions! Here’s how to get involved:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.nio.file.Path;

/**
* Describes a semantic RDF serialization format, extending file-level metadata
Expand Down Expand Up @@ -148,6 +149,44 @@ public static Optional<RDFFormat> byExtension(String extension) {
.findFirst();
}

/**
* Looks up a format from the final extension of a file name, ignoring case.
* This is a name-based hint; the file is not opened or inspected. Compression
* suffixes are not interpreted. Ambiguous extensions require an explicit
* format. Canonical N-Quads is treated as N-Quads input.
*
* @param fileName file name, or {@code null}
* @return the matching input format, or empty for an ambiguous, unknown, or missing extension
*/
public static Optional<RDFFormat> forFileName(String fileName) {
if (fileName == null) {
return Optional.empty();
}
int separator = Math.max(fileName.lastIndexOf('/'), fileName.lastIndexOf('\\'));
int dot = fileName.lastIndexOf('.');
if (dot <= separator + 1) {
return Optional.empty();
}
String extension = fileName.substring(dot + 1);
List<RDFFormat> candidates = all().stream()
.filter(format -> format != RDFC_1_0)
.filter(format -> format.getExtensions().stream().anyMatch(extension::equalsIgnoreCase))
.limit(2)
.toList();
return candidates.size() == 1 ? Optional.of(candidates.getFirst()) : Optional.empty();
}

/**
* Looks up a format from a path's file name without accessing the filesystem.
*
* @param path file path, or {@code null}
* @return the matching format, or empty when no supported extension is present
*/
public static Optional<RDFFormat> forPath(Path path) {
return path == null || path.getFileName() == null
? Optional.empty() : forFileName(path.getFileName().toString());
}

/**
* Finds a known RDF format by MIME type (case-insensitive).
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,13 @@ public NQuadsListener(Model model, ValueFactory factory, IOOptions options) {
@Override
public void enterStatement(NQuadsParser.StatementContext ctx) {

checkForInvalidDirectives(ctx);


currentSubject = extractSubject(ctx.subject());
currentPredicate = extractPredicate(ctx.predicate());
currentGraph = (ctx.graphLabel() != null) ? extractGraph(ctx.graphLabel()) : null;
}

/**
* Checks for invalid directives in N-Quads format.
* N-Quads does not support @base or @prefix directives.
*/
private void checkForInvalidDirectives(NQuadsParser.StatementContext ctx) {
String text = ctx.getText();
if (text != null && (text.contains("@base") || text.contains("@prefix"))) {
throw new ParsingException(
"Directives (@base, @prefix) are not allowed in N-Quads format");
}
}
@Override
public void exitStatement(NQuadsParser.StatementContext ctx) {
Value object = extractObject(ctx.object());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
import fr.inria.corese.core.next.data.spi.io.parser.AbstractRDFParser;
import fr.inria.corese.core.next.data.api.io.option.RDFParsingOptions;
import fr.inria.corese.core.next.data.api.exception.ParsingException;
import fr.inria.corese.core.next.data.impl.io.parser.support.LineRDFParser;
import fr.inria.corese.core.next.generated.antlr.NQuadsLexer;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeWalker;

import java.io.IOException;
import java.io.InputStream;
Expand All @@ -20,7 +19,7 @@
/**
* 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.
* then a listener to populate the RDF model one physical line at a time.
*/
public class NQuadsParser extends AbstractRDFParser {

Expand Down Expand Up @@ -65,20 +64,15 @@ public void parse(InputStream in, String baseURI) throws ParsingException {
@Override
public void parse(Reader reader, String baseURI) throws ParsingException {
try {
CharStream charStream = CharStreams.fromReader(reader);

NQuadsLexer lexer = new NQuadsLexer(charStream);
NQuadsLexer lexer = new NQuadsLexer(CharStreams.fromString(""));
configureErrorHandling(lexer);

CommonTokenStream tokens = new CommonTokenStream(lexer);
fr.inria.corese.core.next.generated.antlr.NQuadsParser parser = new fr.inria.corese.core.next.generated.antlr.NQuadsParser(tokens);
configureErrorHandling(parser);

ParseTree tree = parser.nquadsDoc();

ParseTreeWalker walker = new ParseTreeWalker();
NQuadsListener listener = new NQuadsListener(getModel(), getValueFactory(), getConfig());
walker.walk(listener, tree);
LineRDFParser.parse(reader, lexer, parser, parser::nquadsDoc, listener);

} catch (ParsingException e) {
throw e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@
import fr.inria.corese.core.next.data.spi.io.parser.AbstractRDFParser;
import fr.inria.corese.core.next.data.api.io.option.RDFParsingOptions;
import fr.inria.corese.core.next.data.api.exception.ParsingException;
import fr.inria.corese.core.next.data.impl.io.parser.support.LineRDFParser;
import fr.inria.corese.core.next.generated.antlr.NTriplesLexer;
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.ParseTreeWalker;
import org.antlr.v4.runtime.ConsoleErrorListener;
import org.antlr.v4.runtime.misc.ParseCancellationException;
import org.antlr.v4.runtime.BaseErrorListener;
Expand All @@ -28,7 +26,7 @@
/**
* An ANTLR4-based parser for the N-Triples format.
* This parser uses an ANTLR grammar to tokenize and parse N-Triples documents,
* then a listener to build the RDF model.
* then a listener to populate the RDF model one physical line at a time.
*/
public class NTriplesParser extends AbstractRDFParser {

Expand Down Expand Up @@ -73,16 +71,7 @@ public void parse(InputStream in, String baseURI) throws ParsingException {
@Override
public void parse(Reader reader, String baseURI) throws ParsingException {
try {
CharStream charStream = CharStreams.fromReader(reader);
String input = charStream.toString();
if (input.contains("@prefix")) {
throw new ParsingException("@prefix directives are not allowed in N-Triples");
}
if (input.contains("@base")) {
throw new ParsingException("@base directives are not allowed in N-Triples");
}
charStream = CharStreams.fromString(input);
NTriplesLexer lexer = new NTriplesLexer(charStream);
NTriplesLexer lexer = new NTriplesLexer(CharStreams.fromString(""));

lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);
lexer.addErrorListener(new NTriplesErrorListener());
Expand All @@ -95,12 +84,8 @@ public void parse(Reader reader, String baseURI) throws ParsingException {
antlrParser.setErrorHandler(new BailErrorStrategy());
antlrParser.addErrorListener(new NTriplesErrorListener());

ParseTreeWalker walker = new ParseTreeWalker();
ParseTree tree = antlrParser.ntriplesDoc();

NTriplesListener listener = new NTriplesListener(getModel(), getValueFactory(), getConfig());

walker.walk(listener, tree);
LineRDFParser.parse(reader, lexer, antlrParser, antlrParser::ntriplesDoc, listener);

} catch (ParseCancellationException pce) {
if (pce.getCause() instanceof ParsingException cause) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package fr.inria.corese.core.next.data.impl.io.parser.support;

import fr.inria.corese.core.next.data.api.exception.ParsingException;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.ParseTreeWalker;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.util.function.Supplier;

/**
* Runs a line-oriented RDF grammar without retaining the complete input or its
* parse tree. Parser instances are confined to one call; buffers and syntax
* trees are bounded by the largest physical line, plus the reader buffer.
*/
public final class LineRDFParser {

private LineRDFParser() { }

/**
* Parses and emits each complete physical line before reading the next one.
* Valid prefixes may already have been added to the target when parsing fails;
* callers requiring atomicity must supply a transactional target.
*
* @param source caller-owned reader, never closed here
* @param lexer configured lexer, reused for each line
* @param parser configured parser, reused for each line
* @param document grammar entry point on {@code parser}
* @param listener statement-emitting listener, shared across lines
* @throws IOException if reading fails
* @throws ParsingException if a line is invalid or the thread is interrupted
*/
public static void parse(Reader source, Lexer lexer, Parser parser,
Supplier<? extends ParseTree> document, ParseTreeListener listener) throws IOException {
BufferedReader reader = source instanceof BufferedReader buffered ? buffered : new BufferedReader(source);
CommonTokenStream tokens = new CommonTokenStream(lexer);
ParseTreeWalker walker = new ParseTreeWalker();
String line;
int lineNumber = 0;
while ((line = reader.readLine()) != null) {
if (Thread.currentThread().isInterrupted()) {
throw new ParsingException("RDF parsing was interrupted");
}
lexer.setInputStream(CharStreams.fromString(line));
lexer.setLine(++lineNumber);
tokens.setTokenSource(lexer);
parser.setTokenStream(tokens);
ParseTree tree = document.get();
if (tokens.LA(1) != Token.EOF) {
throw new ParsingException("Unexpected RDF content at line " + lineNumber
+ ":" + tokens.LT(1).getCharPositionInLine());
}
walker.walk(listener, tree);
}
}
}
Loading