This is an automated email from the ASF dual-hosted git repository.

afs pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git


The following commit(s) were added to refs/heads/main by this push:
     new 3014246b38 GH-2805: Implement VERSION for N-Triples and N-Quads parsing
3014246b38 is described below

commit 3014246b382d2439f4cd81aa72a7383ba0f9b3d5
Author: Andy Seaborne <[email protected]>
AuthorDate: Thu Oct 8 16:14:21 2026 +0100

    GH-2805: Implement VERSION for N-Triples and N-Quads parsing
---
 .../java/org/apache/jena/riot/lang/LangNQuads.java | 10 ++++-
 .../org/apache/jena/riot/lang/LangNTriples.java    | 14 ++++---
 .../java/org/apache/jena/riot/lang/LangNTuple.java | 46 ++++++++++++++++++++--
 .../org/apache/jena/riot/tokens/StringType.java    |  2 +-
 .../org/apache/jena/riot/lang/TestLangNQuads.java  | 24 +++++++++++
 .../apache/jena/riot/lang/TestLangNTriples.java    | 24 +++++++++++
 6 files changed, 107 insertions(+), 13 deletions(-)

diff --git a/jena-arq/src/main/java/org/apache/jena/riot/lang/LangNQuads.java 
b/jena-arq/src/main/java/org/apache/jena/riot/lang/LangNQuads.java
index 4f60751738..a38d371b1b 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/lang/LangNQuads.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/lang/LangNQuads.java
@@ -53,14 +53,20 @@ public class LangNQuads extends LangNTuple<Quad> {
     @Override
     protected final void runParser() {
         while (hasNext()) {
-            Quad x = parseOne();
+            Quad x = parseOneStatement();
             if ( x != null )
                 dest.quad(x);
         }
     }
 
     @Override
-    protected final Quad parseOne() {
+    protected final Quad parseOneStatement() {
+        if ( parsePossibleVersion() )
+            return null;
+        return parseQuad();
+    }
+
+    protected final Quad parseQuad() {
         Token token = peekToken();
         Node s = parseSubject();
         Node p = parsePredicate();
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/lang/LangNTriples.java 
b/jena-arq/src/main/java/org/apache/jena/riot/lang/LangNTriples.java
index 39c2595943..2123c84fc8 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/lang/LangNTriples.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/lang/LangNTriples.java
@@ -35,32 +35,34 @@ import org.slf4j.LoggerFactory ;
 
 /**
  * N-Triples.
- * 
+ *
  * @see <a 
href="http://www.w3.org/TR/n-triples/";>http://www.w3.org/TR/n-triples/</a>
  */
 public final class LangNTriples extends LangNTuple<Triple>
 {
     private static Logger messageLog = LoggerFactory.getLogger("N-Triples") ;
-    
+
     public LangNTriples(Tokenizer tokens, ParserProfile profile, StreamRDF 
dest) {
         super(tokens, profile, dest) ;
     }
-    
+
     @Override
     public Lang getLang()   { return RDFLanguages.NTRIPLES ; }
 
-    /** Method to parse the whole stream of triples, sending each to the sink 
*/ 
+    /** Method to parse the whole stream of triples, sending each to the sink 
*/
     @Override
     protected final void runParser() {
         while (hasNext()) {
-            Triple x = parseOne();
+            Triple x = parseOneStatement();
             if ( x != null )
                 dest.triple(x);
         }
     }
 
     @Override
-    protected final Triple parseOne() {
+    protected final Triple parseOneStatement() {
+        if ( parsePossibleVersion() )
+            return null;
         Triple triple = parseTriple();
         Token x = nextToken();
         if ( x.getType() != TokenType.DOT )
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/lang/LangNTuple.java 
b/jena-arq/src/main/java/org/apache/jena/riot/lang/LangNTuple.java
index 29210ebca6..defd5ff1c4 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/lang/LangNTuple.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/lang/LangNTuple.java
@@ -69,11 +69,14 @@ public abstract class LangNTuple<X> extends LangBase 
implements Iterator<X>
 
     @Override
     public final X next() {
-        return parseOne();
+        return parseOneStatement();
     }
 
-    /** Parse one tuple - return object to be sent to the sink or null for 
none */
-    protected abstract X parseOne() ;
+    /**
+     * Parse one tuple - return object to be sent to the sink or null for none.
+     * null is returned for VERSION
+     */
+    protected abstract X parseOneStatement() ;
 
     /** Note a tuple not being output */
     protected void skipOne(X object, String printForm, long line, long col) {
@@ -82,9 +85,44 @@ public abstract class LangNTuple<X> extends LangBase 
implements Iterator<X>
 
     protected abstract Node tokenAsNode(Token token) ;
 
+    // NT and NQ VERSION keyword. It must be upper case.
+    protected static String kwVERSION = "VERSION";
+
+    /**
+     * Parse a possible VERSION directive.
+     * Return true for seen keyword,
+     * false for not a keyword, including EOF.
+     * Throw error on bad directive.
+     */
+    protected final boolean parsePossibleVersion() {
+        Token token = peekToken();
+        // Parse version lines.
+        if ( token .isEOF() || ! token.hasType(TokenType.KEYWORD) )
+            return false;
+
+        // Keywords
+        nextToken();
+        String KW = token.getImage();
+        if ( ! KW.equals(kwVERSION) )
+            exception(token, "Keyword at start of triple: %s", token);
+
+        // VERSION
+        Token token2 = peekToken();
+        if ( token2.isEOF() )
+            exception(token, "Expected version string after VERSION keyword");
+        expect("Expected string for VERSION", token2.getType());
+
+        StringType stringType = token2.getStringType();
+        if ( stringType != StringType.STRING2 )
+            exception(token, "Expected \"-quoted string for the version");
+        String versionStr = token2.getImage();
+        dest.version(versionStr);
+        return true;
+    }
+
     // One triple, not including terminator.
     protected final Triple parseTriple() {
-        Token token = peekToken();
+        Token token = peekToken();      // Remember for line/column
         Node s = parseSubject();
         Node p = parsePredicate();
         Node o = parseObject();
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/tokens/StringType.java 
b/jena-arq/src/main/java/org/apache/jena/riot/tokens/StringType.java
index a9ccf50ae6..1b545a79b5 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/tokens/StringType.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/tokens/StringType.java
@@ -25,7 +25,7 @@ package org.apache.jena.riot.tokens;
 public enum StringType {
     /** A string delimited by 1 single-quote character : {@code 'abc'} */
     STRING1,
-    /** A string delimited by 1 double-quote character : {@code 'abc'} */
+    /** A string delimited by 1 double-quote character : {@code "abc"} */
     STRING2,
     /** A string delimited by 3 single-quote characters : {@code '''abc'''} */
     LONG_STRING1,
diff --git 
a/jena-arq/src/test/java/org/apache/jena/riot/lang/TestLangNQuads.java 
b/jena-arq/src/test/java/org/apache/jena/riot/lang/TestLangNQuads.java
index 1ddca8c5fd..0166f268f2 100644
--- a/jena-arq/src/test/java/org/apache/jena/riot/lang/TestLangNQuads.java
+++ b/jena-arq/src/test/java/org/apache/jena/riot/lang/TestLangNQuads.java
@@ -58,6 +58,30 @@ public class TestLangNQuads extends AbstractTestLangNTuples
         parseException(ExFatal.class, "<http://example/x> <http://example/p> 
<http://example/s> <http://example/g>"); // No trailing DOT
     }
 
+    @Test
+    public void nq_version_1() {
+        parseCheck("""
+                VERSION "1.2"
+                <http://example/s> <http://example/p> <http://example/o>  
<http://example/g> .
+                VERSION "1.2"
+                """);
+    }
+
+    @Test
+    public void nq_version_2() {
+        parseCheck("""
+                VERSION "1.2"
+                """);
+    }
+
+    @Test
+    public void nq_version_3() {
+        parseCheck("""
+                VERSION "1.2"
+                VERSION "1.2"
+                """);
+    }
+
     @Test
     public void nq_only_1_no_tuples() {
         parseException(ExFatal.class, "<http://example/x> <http://example/p> 
<http://example/s> <http://example/g> <http://example/c> .");
diff --git 
a/jena-arq/src/test/java/org/apache/jena/riot/lang/TestLangNTriples.java 
b/jena-arq/src/test/java/org/apache/jena/riot/lang/TestLangNTriples.java
index 4482b95d8a..a42e775c60 100644
--- a/jena-arq/src/test/java/org/apache/jena/riot/lang/TestLangNTriples.java
+++ b/jena-arq/src/test/java/org/apache/jena/riot/lang/TestLangNTriples.java
@@ -90,6 +90,30 @@ public class TestLangNTriples extends AbstractTestLangNTuples
         assertTrue(g1.isIsomorphicWith(m1.getGraph()));
     }
 
+    @Test
+    public void nt_version_1() {
+        parseCheck("""
+                VERSION "1.2"
+                <http://example/s> <http://example/p> <http://example/o> .
+                VERSION "1.2"
+                """);
+    }
+
+    @Test
+    public void nt_version_2() {
+        parseCheck("""
+                VERSION "1.2"
+                """);
+    }
+
+    @Test
+    public void nt_version_3() {
+        parseCheck("""
+                VERSION "1.2"
+                VERSION "1.2"
+                """);
+    }
+
     @Test
     public void nt_only_no_quads() {
         parseException(ExFatal.class, "<http://example/x> <http://example/p> 
<http://example/s> <http://example/g> .");

Reply via email to