You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2023/12/23 12:40:41 UTC

(jena) 02/02: GH-2083: Convert default graph quads to triples when writing triples syntax.

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

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

commit f8a677f2c3d10564673e8aad683609a2b79f495e
Author: Andy Seaborne <an...@apache.org>
AuthorDate: Fri Dec 22 14:01:37 2023 +0000

    GH-2083: Convert default graph quads to triples when writing triples syntax.
---
 .../java/org/apache/jena/riot/out/NodeFmtLib.java  |  6 +++
 .../apache/jena/riot/system/StreamRDFWrapper.java  |  3 +-
 .../apache/jena/riot/system/StreamRDFWriter.java   | 10 +++++
 .../apache/jena/riot/system/StreamTriplesOnly.java | 44 ++++++++++++++++++----
 .../jena/riot/writer/WriterStreamRDFBase.java      |  1 -
 jena-cmds/src/main/java/riotcmd/CmdLangParse.java  | 13 ++++---
 6 files changed, 61 insertions(+), 16 deletions(-)

diff --git a/jena-arq/src/main/java/org/apache/jena/riot/out/NodeFmtLib.java b/jena-arq/src/main/java/org/apache/jena/riot/out/NodeFmtLib.java
index 5064fa2e32..60bc9abbdf 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/out/NodeFmtLib.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/out/NodeFmtLib.java
@@ -164,6 +164,12 @@ public class NodeFmtLib
         return displayStrNodes(t.getSubject(), t.getPredicate(), t.getObject());
     }
 
+    public static String displayStr(Quad q) {
+        if ( q == null )
+            return nullStr;
+        return displayStrNodes(q.getGraph(), q.getSubject(), q.getPredicate(), q.getObject());
+    }
+
     public static String displayStr(Node node) {
         if ( node == null )
             return nullStr;
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFWrapper.java b/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFWrapper.java
index ced0853038..e1c68c8984 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFWrapper.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFWrapper.java
@@ -28,9 +28,10 @@ import org.apache.jena.sparql.core.Quad ;
 public class StreamRDFWrapper implements StreamRDF
 {
     protected final StreamRDF other ;
+    public final StreamRDF get() { return other; }
 
     public StreamRDFWrapper(StreamRDF other) { this.other = other ; }
-    
+
     @Override
     public void start()
     { other.start() ; }
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFWriter.java b/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFWriter.java
index 01d231d59c..094b05d0e4 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFWriter.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/system/StreamRDFWriter.java
@@ -225,9 +225,19 @@ public class StreamRDFWriter {
         if ( context == null )
             context = RIOT.getContext().copy();
         StreamRDF stream = x.create(output, format, context);
+        if ( ! RDFLanguages.isQuads(format.getLang()) )
+            stream = triplesOnly(stream);
         return stream ;
     }
 
+
+    /* What to do if a quad is seen when writing in a triples-only syntax. */
+    private static StreamRDF triplesOnly(StreamRDF stream) {
+        // StreamTriplesOnly converts default graph quads to triples.
+        // Otherwise, throw exception.
+        return StreamTriplesOnly.exceptionOnQuads(stream);
+    }
+
     public static boolean registered(Lang lang) {
         RDFFormat fmt = registry.defaultSerialization(lang) ;
         return registry.contains(fmt) ;
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/system/StreamTriplesOnly.java b/jena-arq/src/main/java/org/apache/jena/riot/system/StreamTriplesOnly.java
index b3c1f3dda7..413bcd4fb8 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/system/StreamTriplesOnly.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/system/StreamTriplesOnly.java
@@ -18,6 +18,10 @@
 
 package org.apache.jena.riot.system;
 
+import java.util.function.Function;
+
+import org.apache.jena.riot.RiotException;
+import org.apache.jena.riot.out.NodeFmtLib;
 import org.apache.jena.sparql.core.Quad;
 
 /**
@@ -27,27 +31,51 @@ import org.apache.jena.sparql.core.Quad;
  */
 public class StreamTriplesOnly extends StreamRDFWrapper {
 
-    public static StreamRDF actionIfQuads(StreamRDF stream, Runnable action) {
+    public static enum QuadPolicy { CALL, IGNORE }
+
+    /** Exception on non-delfault graph quad */
+    private static Function<Quad, QuadPolicy> actionException = (q) -> {
+        throw new RiotException("Quad in Triple output: "+NodeFmtLib.str(q));
+    };
+
+    /** Replace an existing wrapper with a new policy for quads */
+    public static StreamRDF setActionIfQuads(StreamRDF stream, Function<Quad, QuadPolicy> action) {
+        // Strip existing layers.
+        while ( stream instanceof StreamTriplesOnly stream2 ) {
+            stream = stream2.get();
+        }
+        return addActionIfQuads(stream, action);
+    }
+
+    /** Add a new policy layer for quads */
+    public static StreamRDF addActionIfQuads(StreamRDF stream, Function<Quad, QuadPolicy> action) {
         return new StreamTriplesOnly(stream, action);
     }
 
-    private boolean seenQuads = false;
-    private final Runnable action;
+    /** Throw a {@link RiotException} if a non-default graph quad is seen. */
+    public static StreamRDF exceptionOnQuads(StreamRDF stream) {
+        return new StreamTriplesOnly(stream, actionException);
+    }
+
+    private QuadPolicy quadAction = QuadPolicy.CALL;
+    // The policy when seeing a quad that isn't in the default graph.
+    // Return true for "continue calling"
+    // Return false for "don't call again, ignore from now on"
+    private final Function<Quad, QuadPolicy> action;
 
-    private StreamTriplesOnly(StreamRDF sink, Runnable action) {
+    private StreamTriplesOnly(StreamRDF sink, Function<Quad, QuadPolicy> action) {
         super(sink) ;
         this.action = action;
     }
 
     @Override
     public void quad(Quad quad) {
-        if ( quad.isTriple() || quad.isDefaultGraph() || quad.isUnionGraph() ) {
+        if ( quad.getGraph() == null || quad.isTriple() || quad.isDefaultGraph() ) {
             triple(quad.asTriple()) ;
             return;
         }
-        if ( ! seenQuads ) {
-            action.run();
-            seenQuads = true;
+        if ( quadAction == QuadPolicy.CALL ) {
+            quadAction = action.apply(quad);
         }
     }
 
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/writer/WriterStreamRDFBase.java b/jena-arq/src/main/java/org/apache/jena/riot/writer/WriterStreamRDFBase.java
index f4aba272a7..cd826bb35d 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/writer/WriterStreamRDFBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/writer/WriterStreamRDFBase.java
@@ -47,7 +47,6 @@ import org.apache.jena.sparql.util.Context;
  * <p>
  * For N-Quads and N-triples use {@link WriterStreamRDFPlain}.
  */
-
 public abstract class WriterStreamRDFBase implements StreamRDF
 {
     // What did we do last?
diff --git a/jena-cmds/src/main/java/riotcmd/CmdLangParse.java b/jena-cmds/src/main/java/riotcmd/CmdLangParse.java
index 2f9d1a4a66..a682697ce3 100644
--- a/jena-cmds/src/main/java/riotcmd/CmdLangParse.java
+++ b/jena-cmds/src/main/java/riotcmd/CmdLangParse.java
@@ -23,6 +23,7 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.function.Function;
 import java.util.zip.GZIPOutputStream;
 
 import arq.cmdline.ModContext;
@@ -82,12 +83,12 @@ public abstract class CmdLangParse extends CmdGeneral {
     }
 
     protected List<ParseRecord> outcomes = new ArrayList<>();
-
     protected OutputStream outputWrite = System.out;
     protected StreamRDF parserOutputStream = null;
     protected String parserBaseIRI = null;
     protected boolean passRelativeURIs = false;
     protected String writerBaseIRI = null;
+
     @Override
     protected void processModulesAndArgs() {
         cmdStrictMode = super.contains(argStrict);
@@ -202,18 +203,18 @@ public abstract class CmdLangParse extends CmdGeneral {
 
         if ( ! modLangParse.mergeQuads() && ! isQuadsOutput() ) {
             // Only pass through triples.
-            final StreamRDF dest = parserOutputStream;
             if ( isStreamingOutput() ) {
-                Runnable action = () -> {
+                Function<Quad, StreamTriplesOnly.QuadPolicy> action = (quad) -> {
                     // dest may be significantly buffered over the top of the output stream.
                     // The log message does not necessarily come out in the right place - it may be early.
+                    // "Best effort" attempt to align log message to output.
                     IO.flush(outputWrite);
                     Log.warn(SysRIOT.getLogger(), "Quads in triples output - quads ignored.");
+                    return StreamTriplesOnly.QuadPolicy.IGNORE;
                 };
-                parserOutputStream = StreamTriplesOnly.actionIfQuads(parserOutputStream, action);
-            } else {
-                // Not streaming - code can issue error before formatting.
+                parserOutputStream = StreamTriplesOnly.setActionIfQuads(parserOutputStream, action);
             }
+            // If not streaming, leave to the writer,
         }
 
         // If QuadsToTriples is added here, then counts will be "triples only"