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 c2b57a7d48a5f973f588cf9e6b482fb073f19d7f
Author: Andy Seaborne <[email protected]>
AuthorDate: Mon Apr 6 10:14:53 2026 +0100

    GH-3835: N-Triples and N-Quads canonical output format
---
 .../main/java/org/apache/jena/riot/RDFFormat.java  |   7 +
 .../org/apache/jena/riot/RDFWriterRegistry.java    |  78 ++++----
 .../org/apache/jena/riot/writer/NQuadsWriter.java  |   2 +-
 .../jena/riot/writer/c14n/DatasetGraphOrdered.java | 194 ++++++++++++++++++++
 .../jena/riot/writer/c14n/EscapeStr_C14N.java      | 203 +++++++++++++++++++++
 .../apache/jena/riot/writer/c14n/GraphOrdered.java |  78 ++++++++
 .../jena/riot/writer/c14n/NQuadsWriter_C14N.java   |  32 ++++
 .../jena/riot/writer/c14n/NTriplesWriter_C14N.java |  32 ++++
 .../jena/riot/writer/c14n/NodeFormatter_C14N.java  | 104 +++++++++++
 .../jena/sparql/core/DatasetGraphCollection.java   |  17 +-
 .../apache/jena/sparql/core/DatasetGraphQuads.java |   3 +-
 .../java/org/apache/jena/arq/ARQTestSuite.java     |   2 +-
 .../apache/jena/arq/junit/riot/RiotC14NTest.java   |  95 ++++++----
 .../apache/jena/arq/junit/riot/VocabLangRDF.java   |   8 +-
 .../jena/arq/junit/textrunner/TextTestRunner.java  |   6 +-
 .../riot/{Scripts_c14n.java => Scripts_C14N.java}  |   8 +-
 .../jena/riot/Scripts_RIOT_rdf_tests_std.java      |   2 +-
 .../main/java/org/apache/jena/atlas/lib/Bytes.java |  70 +++----
 18 files changed, 804 insertions(+), 137 deletions(-)

diff --git a/jena-arq/src/main/java/org/apache/jena/riot/RDFFormat.java 
b/jena-arq/src/main/java/org/apache/jena/riot/RDFFormat.java
index 35d6ef0ecd..ff98e61ab6 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/RDFFormat.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/RDFFormat.java
@@ -46,6 +46,9 @@ public class RDFFormat {
     /** Variant for RDF Thrift using values */
     public static final RDFFormatVariant ValueEncoding  = new 
RDFFormatVariant("Value") ;
 
+    /** Variant for RDF Canonicalization output (N-Triples and N-Quads) */
+    public static final RDFFormatVariant CANONICAL      = new 
RDFFormatVariant("canonical");
+
     /** Turtle - pretty form */
     public static final RDFFormat TURTLE_PRETTY     = new 
RDFFormat(Lang.TURTLE, PRETTY) ;
     /** Turtle - default form */
@@ -69,6 +72,8 @@ public class RDFFormat {
     public static final RDFFormat  NTRIPLES_ASCII   = new 
RDFFormat(Lang.NTRIPLES, ASCII) ;
     /** N-Triples in UTF-8 with short blank node identifiers */
     public static final RDFFormat  NTRIPLES_PRETTY = new 
RDFFormat(Lang.NTRIPLES, PRETTY) ;
+    /** N-Triples in UTF-8 and canonical form. */
+    public static final RDFFormat  NTRIPLES_C14N   = new 
RDFFormat(Lang.NTRIPLES, CANONICAL) ;
 
     /** N-Quads in UTF-8 */
     public static final RDFFormat  NQUADS_UTF8      = new 
RDFFormat(Lang.NQUADS, UTF8) ;
@@ -80,6 +85,8 @@ public class RDFFormat {
     public static final RDFFormat  NQUADS_ASCII     = new 
RDFFormat(Lang.NQUADS, ASCII) ;
     /** N-Quads in UTF-8 with short blank node identifiers */
     public static final RDFFormat  NQUADS_PRETTY    = new 
RDFFormat(Lang.NTRIPLES, PRETTY) ;
+    /** N-Quads in UTF-8 and canonical form. */
+    public static final RDFFormat  NQUADS_C14N      = new 
RDFFormat(Lang.NTRIPLES, CANONICAL) ;
 
     /** TriG - pretty form */
     public static final RDFFormat  TRIG_PRETTY      = new RDFFormat(Lang.TRIG, 
PRETTY) ;
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/RDFWriterRegistry.java 
b/jena-arq/src/main/java/org/apache/jena/riot/RDFWriterRegistry.java
index 1ccbc3d3bb..260207c795 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/RDFWriterRegistry.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/RDFWriterRegistry.java
@@ -31,6 +31,8 @@ import org.apache.jena.riot.system.StreamRDFWriter;
 import org.apache.jena.riot.thrift.WriterDatasetThrift;
 import org.apache.jena.riot.thrift.WriterGraphThrift;
 import org.apache.jena.riot.writer.*;
+import org.apache.jena.riot.writer.c14n.NQuadsWriter_C14N;
+import org.apache.jena.riot.writer.c14n.NTriplesWriter_C14N;
 import org.apache.jena.sys.JenaSystem;
 
 /**
@@ -80,6 +82,8 @@ public class RDFWriterRegistry
                 return new NTriplesWriter(CharSpace.ASCII);
             if ( Objects.equals(RDFFormat.NTRIPLES_PRETTY, serialization) )
                 return new NTriplesWriterPretty();
+            if ( Objects.equals(RDFFormat.NTRIPLES_C14N, serialization) )
+                return new NTriplesWriter_C14N();
 
             if ( Objects.equals(RDFFormat.RDFJSON, serialization) )
                 return new RDFJSONWriter();
@@ -112,6 +116,8 @@ public class RDFWriterRegistry
                 return new NQuadsWriter(CharSpace.ASCII);
             if ( Objects.equals(RDFFormat.NQUADS_PRETTY, serialization) )
                 return new NQuadsWriterPretty();
+            if ( Objects.equals(RDFFormat.NQUADS_C14N, serialization) )
+                return new NQuadsWriter_C14N();
 
             if ( Objects.equals(RDFFormat.RDFNULL, serialization) )
                 return NullWriter.factory.create(RDFFormat.RDFNULL);
@@ -173,61 +179,63 @@ public class RDFWriterRegistry
         register(RDFFormat.NTRIPLES,        wgfactory);
         register(RDFFormat.NTRIPLES_ASCII,  wgfactory);
         register(RDFFormat.NTRIPLES_PRETTY, wgfactory);
+        register(RDFFormat.NTRIPLES_C14N,   wgfactory);
 
         // JSON-LD 1.1
-        register(RDFFormat.JSONLD11,                    wgJsonldFactory11);
-        register(RDFFormat.JSONLD11_PRETTY,             wgJsonldFactory11);
-        register(RDFFormat.JSONLD11_PLAIN,              wgJsonldFactory11);
-        register(RDFFormat.JSONLD11_FLAT,               wgJsonldFactory11);
+        register(RDFFormat.JSONLD11,        wgJsonldFactory11);
+        register(RDFFormat.JSONLD11_PRETTY, wgJsonldFactory11);
+        register(RDFFormat.JSONLD11_PLAIN,  wgJsonldFactory11);
+        register(RDFFormat.JSONLD11_FLAT,   wgJsonldFactory11);
 
-        register(RDFFormat.JSONLD11,                    wdsJsonldFactory11);
-        register(RDFFormat.JSONLD11_PRETTY,             wdsJsonldFactory11);
-        register(RDFFormat.JSONLD11_PLAIN,              wdsJsonldFactory11);
-        register(RDFFormat.JSONLD11_FLAT,               wdsJsonldFactory11);
+        register(RDFFormat.JSONLD11,        wdsJsonldFactory11);
+        register(RDFFormat.JSONLD11_PRETTY, wdsJsonldFactory11);
+        register(RDFFormat.JSONLD11_PLAIN,  wdsJsonldFactory11);
+        register(RDFFormat.JSONLD11_FLAT,   wdsJsonldFactory11);
 
         // JSON-LD System defaults.
-        register(RDFFormat.JSONLD,                      
jsonldWriterGraphDefault);
-        register(RDFFormat.JSONLD_PRETTY,               
jsonldWriterGraphDefault);
-        register(RDFFormat.JSONLD_PLAIN,                
jsonldWriterGraphDefault);
-        register(RDFFormat.JSONLD_FLAT,                 
jsonldWriterGraphDefault);
+        register(RDFFormat.JSONLD,          jsonldWriterGraphDefault);
+        register(RDFFormat.JSONLD_PRETTY,   jsonldWriterGraphDefault);
+        register(RDFFormat.JSONLD_PLAIN,    jsonldWriterGraphDefault);
+        register(RDFFormat.JSONLD_FLAT,     jsonldWriterGraphDefault);
 
-        register(RDFFormat.JSONLD,                      
jsonldWriterDatasetDefault);
-        register(RDFFormat.JSONLD_PRETTY,               
jsonldWriterDatasetDefault);
-        register(RDFFormat.JSONLD_PLAIN,                
jsonldWriterDatasetDefault);
-        register(RDFFormat.JSONLD_FLAT,                 
jsonldWriterDatasetDefault);
+        register(RDFFormat.JSONLD,          jsonldWriterDatasetDefault);
+        register(RDFFormat.JSONLD_PRETTY,   jsonldWriterDatasetDefault);
+        register(RDFFormat.JSONLD_PLAIN,    jsonldWriterDatasetDefault);
+        register(RDFFormat.JSONLD_FLAT,     jsonldWriterDatasetDefault);
 
-        register(RDFFormat.RDFJSON,        wgfactory);
+        register(RDFFormat.RDFJSON,         wgfactory);
 
-        register(RDFFormat.RDFXML_PRETTY,  wgfactory);
-        register(RDFFormat.RDFXML_PLAIN,   wgfactory);
+        register(RDFFormat.RDFXML_PRETTY,   wgfactory);
+        register(RDFFormat.RDFXML_PLAIN,    wgfactory);
 
         // Graphs in a quad format.
-        register(RDFFormat.TRIG_PRETTY,    wgfactory);
-        register(RDFFormat.TRIG_BLOCKS,    wgfactory);
-        register(RDFFormat.TRIG_FLAT,      wgfactory);
-        register(RDFFormat.TRIG_LONG,      wgfactory);
+        register(RDFFormat.TRIG_PRETTY,     wgfactory);
+        register(RDFFormat.TRIG_BLOCKS,     wgfactory);
+        register(RDFFormat.TRIG_FLAT,       wgfactory);
+        register(RDFFormat.TRIG_LONG,       wgfactory);
 
-        register(RDFFormat.NQUADS,         wgfactory);
-        register(RDFFormat.NQUADS_ASCII,   wgfactory);
-        register(RDFFormat.NQUADS_PRETTY,  wgfactory);
-        register(RDFFormat.RDFNULL,        wgfactory);
+        register(RDFFormat.NQUADS,          wgfactory);
+        register(RDFFormat.NQUADS_ASCII,    wgfactory);
+        register(RDFFormat.NQUADS_PRETTY,   wgfactory);
+        register(RDFFormat.NQUADS_C14N,     wgfactory);
 
         register(RDFFormat.RDF_PROTO,           wgProtoFactory);
         register(RDFFormat.RDF_PROTO_VALUES,    wgProtoFactory);
         register(RDFFormat.RDF_THRIFT,          wgThriftFactory);
         register(RDFFormat.RDF_THRIFT_VALUES,   wgThriftFactory);
 
-        register(RDFFormat.TRIX, wgTriXFactory);
+        register(RDFFormat.TRIX,            wgTriXFactory);
+        register(RDFFormat.RDFNULL,         wgfactory);
 
         // Writer factories - datasets.
 
-        register(RDFFormat.TRIG_PRETTY,    wdsfactory);
-        register(RDFFormat.TRIG_BLOCKS,    wdsfactory);
-        register(RDFFormat.TRIG_FLAT,      wdsfactory);
+        register(RDFFormat.TRIG_PRETTY,     wdsfactory);
+        register(RDFFormat.TRIG_BLOCKS,     wdsfactory);
+        register(RDFFormat.TRIG_FLAT,       wdsfactory);
 
-        register(RDFFormat.NQUADS,         wdsfactory);
-        register(RDFFormat.NQUADS_ASCII,   wdsfactory);
-        register(RDFFormat.RDFNULL,        wdsfactory);
+        register(RDFFormat.NQUADS,          wdsfactory);
+        register(RDFFormat.NQUADS_ASCII,    wdsfactory);
+        register(RDFFormat.NQUADS_C14N,     wdsfactory);
 
         register(RDFFormat.RDF_PROTO,           wdsProtoFactory);
         register(RDFFormat.RDF_PROTO_VALUES,    wdsProtoFactory);
@@ -235,6 +243,8 @@ public class RDFWriterRegistry
         register(RDFFormat.RDF_THRIFT_VALUES,   wdsThriftFactory);
 
         register(RDFFormat.TRIX, wdsTriXFactory);
+        register(RDFFormat.RDFNULL,        wdsfactory);
+
     }
 
      // ---- Compatibility
diff --git 
a/jena-arq/src/main/java/org/apache/jena/riot/writer/NQuadsWriter.java 
b/jena-arq/src/main/java/org/apache/jena/riot/writer/NQuadsWriter.java
index 6e9abd6ffe..96352ee16d 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/writer/NQuadsWriter.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/writer/NQuadsWriter.java
@@ -69,7 +69,7 @@ public class NQuadsWriter extends WriterDatasetRIOTBase {
         s.finish();
     }
 
-    private final CharSpace charSpace;
+    protected final CharSpace charSpace;
 
     public NQuadsWriter() {
         this(CharSpace.UTF8);
diff --git 
a/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/DatasetGraphOrdered.java
 
b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/DatasetGraphOrdered.java
new file mode 100644
index 0000000000..4051d5c7b2
--- /dev/null
+++ 
b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/DatasetGraphOrdered.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *   SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.riot.writer.c14n;
+
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.ReadWrite;
+import org.apache.jena.query.TxnType;
+import org.apache.jena.riot.system.PrefixMap;
+import org.apache.jena.riot.system.PrefixMapFactory;
+import org.apache.jena.sparql.core.*;
+
+/**
+ * A simple dataset implementation that preserves the order of quads.
+ * {@link DatasetGraph#find()} will return in insertion order.
+ */
+public class DatasetGraphOrdered extends DatasetGraphQuads {
+
+    private LinkedHashSet<Quad> quads = new LinkedHashSet<>();
+    private PrefixMap prefixMap = PrefixMapFactory.create();
+
+    @Override
+    public PrefixMap prefixes() {
+        return prefixMap;
+    }
+
+    @Override
+    public Iterator<Quad> find() {
+        return quads.iterator();
+    }
+
+    @Override
+    public Iterator<Quad> find(Node g, Node s, Node p, Node o) {
+        Iterator<Quad> iterator = Iter.filter(quads.iterator(), quad -> 
matches(quad, g, s, p, o));
+        return iterator;
+    }
+
+    @Override
+    public Iterator<Quad> findNG(Node g, Node s, Node p, Node o) {
+       Iterator<Quad> iterator = Iter.filter(quads.iterator(),
+                                             quad -> (matches(quad, g, s, p, 
o) && ! Quad.isDefaultGraph(g)));
+        return iterator;
+    }
+
+    @Override
+    public void add(Quad quad) {
+        quads.add(quad);
+    }
+
+    @Override
+    public void delete(Quad quad) {
+        quads.remove(quad);
+    }
+
+    @Override
+    public Graph getDefaultGraph() {
+        return GraphView.createDefaultGraph(this);
+    }
+
+    @Override
+    public Graph getGraph(Node graphNode) {
+        return GraphView.createNamedGraph(this, graphNode);
+    }
+
+    private static boolean matches(Quad quadData, Node g, Node s, Node p, Node 
o) {
+        return Match.match(quadData, g, s, p, o);
+    }
+
+    // ----
+    private final Transactional txn = TransactionalLock.createMRSW();
+    private final Transactional txn() {
+        return txn;
+    }
+
+    @Override
+    public void begin() {
+        txn().begin();
+    }
+
+    @Override
+    public void begin(TxnType txnType) {
+        txn().begin(txnType);
+    }
+
+    @Override
+    public void begin(ReadWrite mode) {
+        txn().begin(mode);
+    }
+
+    @Override
+    public boolean promote(Promote txnType) {
+        return txn().promote(txnType);
+    }
+
+    @Override
+    public void commit() {
+        txn().commit();
+    }
+
+    @Override
+    public void abort() {
+        txn().abort();
+    }
+
+    @Override
+    public boolean isInTransaction() {
+        return txn().isInTransaction();
+    }
+
+    @Override
+    public void end() {
+        txn().end();
+    }
+
+    @Override
+    public ReadWrite transactionMode() {
+        return txn().transactionMode();
+    }
+
+    @Override
+    public TxnType transactionType() {
+        return txn().transactionType();
+    }
+
+    @Override
+    public boolean supportsTransactions() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsTransactionAbort() {
+        return false;
+    }
+    // ----
+
+    // Secondary indexes?
+//
+// @Override
+// public void performAdd( Triple t ) {
+// triples.add(t);
+// }
+//
+// @Override
+// public void performDelete( Triple t ) {
+// triples.remove(t);
+// }
+//
+// @Override
+// public TransactionHandler getTransactionHandler() {
+// return new TransactionHandlerNull();
+// }
+//
+// @Override
+// public ExtendedIterator<Triple> find() {
+// return WrappedIterator.create(triples.iterator());
+// }
+//
+// @Override
+// protected ExtendedIterator<Triple> graphBaseFind(Triple triplePattern) {
+// Iterator<Triple> iterator = Iter.filter(triples.iterator(),
+// triple->matches(triple, triplePattern));
+// return WrappedIterator.create(iterator);
+// }
+//
+// private static boolean matches(Triple tripleData, Triple triplePattern) {
+// return Match.match(tripleData,
+// triplePattern.getSubject(),
+// triplePattern.getPredicate(),
+// triplePattern.getObject());
+// }
+}
diff --git 
a/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/EscapeStr_C14N.java 
b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/EscapeStr_C14N.java
new file mode 100644
index 0000000000..4e4d090878
--- /dev/null
+++ 
b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/EscapeStr_C14N.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *   SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.riot.writer.c14n;
+
+import static org.apache.jena.riot.system.RiotChars.range;
+
+import org.apache.jena.atlas.AtlasException ;
+import org.apache.jena.atlas.io.AWriter ;
+import org.apache.jena.atlas.io.OutputUtils ;
+import org.apache.jena.atlas.lib.CharSpace;
+import org.apache.jena.atlas.lib.Chars;
+import org.apache.jena.util.XML11Char;
+
+/** String escape utilities */
+public class EscapeStr_C14N
+{
+    /** String escape, with quote escaping, including option for multi-line 3 
quote form. */
+    public static void stringEsc(AWriter out, String s, char quoteChar) {
+        stringEsc(out, s, quoteChar, true, CharSpace.UTF8);
+    }
+
+//    /** String escape, with quote escaping, including option for multi-line 
3 quote form. */
+//    public static void stringEsc(AWriter out, String s, char quoteChar, 
boolean singleLineString) {
+//        stringEsc(out, s, quoteChar, singleLineString, CharSpace.UTF8);
+//    }
+
+    public static void stringEsc(AWriter out, String s, char quoteChar, 
boolean singleLineString, CharSpace charSpace) {
+        boolean ascii = ( CharSpace.ASCII == charSpace ) ;
+        int len = s.length() ;
+        int quotesInARow = 0 ;
+
+        for (int i = 0; i < len; i++) {
+            char c = s.charAt(i);
+            // \\ Escape always possible.
+            if (c == '\\') {
+                out.print('\\') ;
+                out.print(c) ;
+                continue ;
+            }
+
+            // Surrogates - print raw so that UTF-8 is generated.
+            // XML 1.1 includes [#x10000-#x10FFFF]
+            if ( false ) {
+                // With checking which should not be necessary.
+                if ( Character.isHighSurrogate(c) ) {
+                    // Surrogates : value = 0x10000 + (H − 0xD800) × 0x400 + 
(L − 0xDC00)
+                    // i.e. 0x10000 + (H − 0xD800) << 10 + (L − 0xDC00)
+                    // i.e. 1024 "high" surrogates (D800–DBFF) and 1024 "low" 
surrogates (DC00–DFFF)
+                    // making 1,048,576 values (2^20) from 0x10000 (0x400 is 
1024 = 2^10)
+
+                    // But AWriter does not write ints
+                    // s.codePointAt(i);
+
+                    // If we want our own checking messages.
+                    i++;
+                    if (i >= len )
+                        throw new AtlasException("High surrogate at end of 
string");
+                    char c2 = s.charAt(i);
+                    if ( !Character.isLowSurrogate(c2) )
+                        throw new AtlasException("High surrogate not followed 
by low surrogate");
+                    out.write(c);
+                    out.write(c2);
+                    continue;
+                }
+                if ( Character.isLowSurrogate(c) )
+                    throw new AtlasException("Low surrogate not following a 
high surrogate");
+            }
+
+            if ( ! singleLineString ) {
+                // Multiline string.
+                if ( c == quoteChar ) {
+                    quotesInARow++ ;
+                    if ( (quotesInARow == 3) || (!singleLineString && (i == 
len - 1)) ) {
+                        // Always quote the final character for multiline use
+                        // otherwise it will run into the wrapping 3 quotes.
+                        out.print("\\");
+                        out.print(quoteChar);
+                        quotesInARow = 0;
+                        continue;
+                    }
+                } else {
+                    quotesInARow = 0 ;
+                }
+            } else {
+                // Single line.
+                if ( c == quoteChar ) {
+                    out.print("\\"); out.print(c) ; continue ;
+                }
+                switch(c) {
+                    // x08 - BS - Backspace
+                    // x09 - HT - Horizontal tab
+                    // x0A - LF - Line feed = NL - newline
+                    // NOT x0B - VT - Vertical tab
+                    // x0C - FF - Form Feed
+                    // x0D - CR
+                    case '\b':  out.print("\\b"); continue;
+                    case '\t':  out.print("\\t"); continue;
+                    case '\n':  out.print("\\n"); continue;
+                    case '\f':  out.print("\\f"); continue;
+                    case '\r':  out.print("\\r"); continue;
+                    default:    // Drop through
+                }
+            }
+
+            if ( ascii ) {
+                writeCharAsASCII(out, c) ;
+                continue;
+            }
+
+//            if ( c == '\uFFFD' ) {
+//                // Unicode replacement character: write as \-u escape
+//                // The text tokenizer raises warnings on raw U+FFFD. A 
replacement character is generated
+//                // if a decoding error occurs (e.g. ISO-8859-1 passed into 
UTF-8); there is no literal U+FFFD
+//                // in the original input. Written as a unicode escape is not 
treated as a warning.
+//                out.print("\\uFFFD");
+//                continue;
+//            }
+
+            // XML11Char.isXML11Valid(c);
+            //  [2]     Char       ::=      [#x1-#xD7FF] | [#xE000-#xFFFD] | 
[#x10000-#x10FFFF]
+
+            // N-Triples/ N-quads canonical form.
+            // """
+            // Characters in the range from U+0000 to U+0007, VT, characters 
in the range from U+000E to U+001F, DEL,
+            // and characters not matching the Char production from [XML11] 
MUST be represented by UCHAR using a lowercase \ u with 4 HEXes.
+            // """
+
+            // Surrogates would be XML11Char.isXML11Invalid, they get printed 
as \-u
+            // We want to pass then raw to the java->UTF-8 processor.
+            if ( c <= 0x07 || c == Chars.VT || range(c, 0x0E , 0x1F ) || c == 
Chars.DEL || XML11Char.isXML11Invalid(c)) {
+
+                if ( ! Character.isSurrogate(c) ) {
+                    // Only 4 hex here (the Java string would have had 
surrogates pairs).
+                    writeUnicodeEscape(out, c);
+                    continue;
+                }
+            }
+
+            // Normal case!
+            out.print(c);
+        }
+    }
+
+    // Choose between \-U 8 hex and \-u 4 hex.
+    private static void writeUnicodeEscape(AWriter out, int c) {
+        if ( c <= 0xFFFF )
+            writeUnicodeEscape4(out, c);
+        else
+            writeUnicodeEscape8(out, c);
+    }
+
+    private static void writeUnicodeEscape4(AWriter out, int c) {
+        out.print("\\u") ;
+        OutputUtils.printHex(out, c, 4) ;
+    }
+
+    private static void writeUnicodeEscape8(AWriter out, int c) {
+        out.print("\\U");
+        OutputUtils.printHex(out, c, 8);
+    }
+
+    /** Write a string with Unicode to ASCII conversion using \-u escapes */
+    public static void writeASCII(AWriter out, String s) {
+        int len = s.length() ;
+        for (int i = 0; i < len; i++) {
+            char c = s.charAt(i);
+            writeCharAsASCII(out, c);
+        }
+    }
+
+    /** Write a character with Unicode to ASCII conversion using \-u escapes */
+    public static void writeCharAsASCII(AWriter out, char c) {
+        if ( c >= 32 && c < 127 )
+            out.print(c);
+        else {
+            // Outside the charset range.
+            // Does not cover beyond 16 bits codepoints directly
+            // (i.e. \U escapes) but Java keeps these as surrogate
+            // pairs and will print as characters
+            out.print("\\u") ;
+            OutputUtils.printHex(out, c, 4) ;
+        }
+    }
+}
\ No newline at end of file
diff --git 
a/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/GraphOrdered.java 
b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/GraphOrdered.java
new file mode 100644
index 0000000000..f8491a00a5
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/GraphOrdered.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *   SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.riot.writer.c14n;
+
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.TransactionHandler;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.graph.impl.GraphBase;
+import org.apache.jena.sparql.core.Match;
+import org.apache.jena.sparql.graph.TransactionHandlerNull;
+import org.apache.jena.util.iterator.ExtendedIterator;
+import org.apache.jena.util.iterator.WrappedIterator;
+
+/**
+ * A simple graph implementation that preserves the order of triples.
+ * {@link Graph#find()} will return in insertion order.
+ */
+public class GraphOrdered extends GraphBase {
+
+    private LinkedHashSet<Triple> triples = new LinkedHashSet<>();
+    // Secondary indexes?
+
+    @Override
+    public void performAdd(Triple t) {
+        triples.add(t);
+    }
+
+    @Override
+    public void performDelete(Triple t) {
+        triples.remove(t);
+    }
+
+    @Override
+    public TransactionHandler getTransactionHandler() {
+        return new TransactionHandlerNull();
+    }
+
+    @Override
+    public ExtendedIterator<Triple> find() {
+        return WrappedIterator.create(triples.iterator());
+    }
+
+    @Override
+    protected ExtendedIterator<Triple> graphBaseFind(Triple triplePattern) {
+        Iterator<Triple> iterator = Iter.filter(triples.iterator(), 
triple->matches(triple, triplePattern));
+        return WrappedIterator.create(iterator);
+    }
+
+    private static boolean matches(Triple tripleData, Triple triplePattern) {
+        return Match.match(tripleData,
+                           triplePattern.getSubject(),
+                           triplePattern.getPredicate(),
+                           triplePattern.getObject());
+    }
+}
diff --git 
a/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/NQuadsWriter_C14N.java
 
b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/NQuadsWriter_C14N.java
new file mode 100644
index 0000000000..700dde4c59
--- /dev/null
+++ 
b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/NQuadsWriter_C14N.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *   SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.riot.writer.c14n;
+
+import org.apache.jena.riot.out.NodeFormatter;
+import org.apache.jena.riot.writer.NQuadsWriter;
+
+public class NQuadsWriter_C14N extends NQuadsWriter {
+    @Override
+    protected NodeFormatter createNodeFormatter() {
+        return new NodeFormatter_C14N(charSpace);
+    }
+}
diff --git 
a/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/NTriplesWriter_C14N.java
 
b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/NTriplesWriter_C14N.java
new file mode 100644
index 0000000000..2894eaa51a
--- /dev/null
+++ 
b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/NTriplesWriter_C14N.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *   SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.riot.writer.c14n;
+
+import org.apache.jena.riot.out.NodeFormatter;
+import org.apache.jena.riot.writer.NTriplesWriter;
+
+public class NTriplesWriter_C14N extends NTriplesWriter {
+    @Override
+    protected NodeFormatter createNodeFormatter() {
+        return new NodeFormatter_C14N(charSpace);
+    }
+}
diff --git 
a/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/NodeFormatter_C14N.java
 
b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/NodeFormatter_C14N.java
new file mode 100644
index 0000000000..4a1b008759
--- /dev/null
+++ 
b/jena-arq/src/main/java/org/apache/jena/riot/writer/c14n/NodeFormatter_C14N.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ *   SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.apache.jena.riot.writer.c14n;
+
+import org.apache.jena.atlas.io.AWriter;
+import org.apache.jena.atlas.lib.CharSpace;
+import org.apache.jena.atlas.lib.EscapeStr;
+import org.apache.jena.riot.out.NodeFormatterBase;
+import org.apache.jena.riot.out.quoted.QuotedStringOutput;
+import org.apache.jena.riot.out.quoted.QuotedStringOutputNT;
+import org.apache.jena.riot.out.quoted.QuotedURI;
+
+/** Formatting for canonical NTriples/NQuads */
+public class NodeFormatter_C14N extends NodeFormatterBase
+{
+    private final QuotedStringOutput quotedStringProc;
+    private final QuotedURI quotedUriProc;
+
+    public NodeFormatter_C14N() { this(CharSpace.UTF8); }
+
+    public NodeFormatter_C14N(CharSpace charSpace) {
+        quotedStringProc = new QuotedStringOutputNT(charSpace) {
+            @Override
+            public void writeStr(AWriter writer, String str) {
+                // Only " strings in N-Triples/N-Quads
+                writer.print(getQuoteChar());
+                // C14N
+                EscapeStr_C14N.stringEsc(writer, str, getQuoteChar(), true, 
charSpace);
+                writer.print(getQuoteChar());
+            }
+        };
+        quotedUriProc = new QuotedURI(charSpace);
+    }
+
+    @Override
+    public void formatURI(AWriter w, String uriStr) {
+        quotedUriProc.writeURI(w, uriStr);
+    }
+
+    @Override
+    public void formatVar(AWriter w, String name) {
+        w.print('?');
+        EscapeStr.stringEsc(w, name, false);
+    }
+
+    @Override
+    public void formatBNode(AWriter w, String label) {
+        w.print("_:");
+        // C14N : Write label as given. Assumes label is valid.
+        String lab = label;
+        w.print(lab);
+    }
+
+    @Override
+    public void formatLitString(AWriter w, String lex) {
+        writeEscaped(w, lex);
+    }
+
+    private void writeEscaped(AWriter w, String lex) {
+        quotedStringProc.writeStr(w, lex);
+    }
+
+    @Override
+    public void formatLitLang(AWriter w, String lex, String langTag) {
+        writeEscaped(w, lex);
+        w.print('@');
+        w.print(langTag);
+    }
+
+    @Override
+    public void formatLitLangDir(AWriter w, String lex, String langTag, String 
direction) {
+        writeEscaped(w, lex);
+        w.print('@');
+        w.print(langTag);
+        w.print("--");
+        w.print(direction);
+    }
+
+    @Override
+    public void formatLitDT(AWriter w, String lex, String datatypeURI) {
+        writeEscaped(w, lex);
+        w.print("^^");
+        formatURI(w, datatypeURI);
+    }
+}
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphCollection.java
 
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphCollection.java
index 35c10c17c0..56bd86ba87 100644
--- 
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphCollection.java
+++ 
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphCollection.java
@@ -32,9 +32,10 @@ import org.apache.jena.graph.Node ;
 import org.apache.jena.shared.JenaException ;
 import org.apache.jena.system.G;
 
-/** Base class for implementations of a DatasetGraph as a set of graphs.
+/**
+ * Base class for implementations of a DatasetGraph as a set of graphs.
  * This can be a fixed collection or a changeable collection depending
- * on the implementation of getDefaultGraph()/getGraph(Node)  
+ * on the implementation of getDefaultGraph()/getGraph(Node)
  */
 public abstract class DatasetGraphCollection extends DatasetGraphBaseFind
 {
@@ -55,13 +56,13 @@ public abstract class DatasetGraphCollection extends 
DatasetGraphBaseFind
             throw new JenaException("No such graph: "+quad.getGraph()) ;
         g.delete(quad.asTriple()) ;
     }
-    
+
     @Override
     protected Iterator<Quad> findInDftGraph(Node s, Node p , Node o)
     {
         return G.triples2quadsDftGraph(getDefaultGraph().find(s, p, o)) ;
     }
-    
+
     @Override
     protected Iter<Quad> findInSpecificNamedGraph(Node g, Node s, Node p , 
Node o)
     {
@@ -78,7 +79,7 @@ public abstract class DatasetGraphCollection extends 
DatasetGraphBaseFind
         IteratorConcat<Quad> iter = new IteratorConcat<>() ;
 
         // Named graphs
-        for ( ; gnames.hasNext() ; )  
+        for ( ; gnames.hasNext() ; )
         {
             Node gn = gnames.next();
             Iterator<Quad> qIter = findInSpecificNamedGraph(gn, s, p, o) ;
@@ -87,13 +88,13 @@ public abstract class DatasetGraphCollection extends 
DatasetGraphBaseFind
         }
         return iter ;
     }
-    
+
     @Override
     public abstract Iterator<Node> listGraphNodes() ;
 
     @Override
     public void clear() {
-        // Delete all triples in the default graph 
+        // Delete all triples in the default graph
         getDefaultGraph().clear() ;
         // Now remove the named graphs (but don't clear them - they may be 
shared).
         List<Node> gnList = Iter.toList(listGraphNodes()) ;
@@ -101,7 +102,7 @@ public abstract class DatasetGraphCollection extends 
DatasetGraphBaseFind
             removeGraph(gn) ;
         }
     }
-    
+
     protected Graph fetchGraph(Node gn)
     {
         if ( Quad.isDefaultGraph(gn) || Objects.equals(gn,Quad.tripleInQuad)) 
// Not preferred style
diff --git 
a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphQuads.java 
b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphQuads.java
index bfd70daadb..f755d4db2e 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphQuads.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphQuads.java
@@ -27,7 +27,8 @@ import org.apache.jena.graph.Graph;
 import org.apache.jena.graph.Node ;
 
 
-/** A DatasetGraph base class for pure quad-centric storage.
+/**
+ * A DatasetGraph base class for pure quad-centric storage.
  */
 public abstract class DatasetGraphQuads extends DatasetGraphBase
 {
diff --git a/jena-arq/src/test/java/org/apache/jena/arq/ARQTestSuite.java 
b/jena-arq/src/test/java/org/apache/jena/arq/ARQTestSuite.java
index 51ab6c0335..36d62b077b 100644
--- a/jena-arq/src/test/java/org/apache/jena/arq/ARQTestSuite.java
+++ b/jena-arq/src/test/java/org/apache/jena/arq/ARQTestSuite.java
@@ -69,7 +69,7 @@ import org.apache.jena.util.TS_UtilsARQ;
     Scripts_RIOT_extra.class,
     Scripts_AltTurtle.class,
 
-    Scripts_c14n.class,
+    Scripts_C14N.class,
 
     // ARQ, SPARQL 1.0, SPARQL 1.1, SPARQL 1.2 - main engine, default 
in-memory dataset.
     Scripts_SPARQL.class,
diff --git 
a/jena-arq/src/test/java/org/apache/jena/arq/junit/riot/RiotC14NTest.java 
b/jena-arq/src/test/java/org/apache/jena/arq/junit/riot/RiotC14NTest.java
index 2b1d0a9ce2..ff564a6e73 100644
--- a/jena-arq/src/test/java/org/apache/jena/arq/junit/riot/RiotC14NTest.java
+++ b/jena-arq/src/test/java/org/apache/jena/arq/junit/riot/RiotC14NTest.java
@@ -24,19 +24,26 @@ package org.apache.jena.arq.junit.riot;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.function.Consumer;
 
 import org.apache.jena.arq.junit.manifest.AbstractManifestTest;
 import org.apache.jena.arq.junit.manifest.ManifestEntry;
-import org.apache.jena.atlas.io.IO;
+import org.apache.jena.atlas.io.IOX;
+import org.apache.jena.atlas.lib.Bytes;
 import org.apache.jena.atlas.lib.IRILib;
 import org.apache.jena.graph.Graph;
 import org.apache.jena.riot.*;
+import org.apache.jena.riot.lang.LabelToNode;
+import org.apache.jena.riot.system.ErrorHandlerFactory;
 import org.apache.jena.riot.system.StreamRDF;
 import org.apache.jena.riot.system.StreamRDFLib;
+import org.apache.jena.riot.writer.c14n.DatasetGraphOrdered;
+import org.apache.jena.riot.writer.c14n.GraphOrdered;
 import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.sparql.core.DatasetGraphFactory;
-import org.apache.jena.sparql.graph.GraphFactory;
 
 public class RiotC14NTest extends AbstractManifestTest {
 
@@ -58,11 +65,10 @@ public class RiotC14NTest extends AbstractManifestTest {
         input = entry.getAction().getURI();
         output = positiveTest ? entry.getResult().getURI() : null;
 
-        boolean silentWarnings = RiotTestsConfig.allowWarnings(manifestEntry);
+        boolean silentWarnings = true; 
//RiotTestsConfig.allowWarnings(manifestEntry);
         parser = ( baseIRI != null )
             ? ParsingStepForTest.parse(input, baseIRI, lang, silentWarnings)
             : ParsingStepForTest.parse(input, lang, silentWarnings);
-
     }
 
     @Override
@@ -73,74 +79,89 @@ public class RiotC14NTest extends AbstractManifestTest {
             run4();
     }
 
-    public void run3()
-    {
-        Graph graph = GraphFactory.createGraphMem();
+    public void run3() {
+        Graph graph = new GraphOrdered();
         StreamRDF dest = StreamRDFLib.graph(graph);
         try {
-            parser.accept(dest);
+            
RDFParser.create().errorHandler(ErrorHandlerFactory.errorHandlerIgnoreWarnings(null))
+                .strict(true).forceLang(lang).base(baseIRI).source(input)
+                // ******
+                .labelToNode(LabelToNode.createUseLabelAsGiven())
+                .parse(dest);
 
             Lang outLang = RDFLanguages.filenameToLang(output, Lang.NTRIPLES);
 
-            // Exactly this string.
-            String actual = 
RDFWriter.source(graph).format(RDFFormat.NTRIPLES_UTF8).asString();
-            String expected;
+            // Special writer. NTriplesWriter_C14N / NodeFormatter_C14N / 
EscapeStr_C14N
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            
RDFWriter.source(graph).format(RDFFormat.NTRIPLES_C14N).output(out);
+
+            byte[] actual = out.toByteArray();
+            byte[] expected;
             try {
                 expected = readFile(output);
             } catch (RiotException ex) {
                 fail("Failed to read results: "+ex.getMessage());
                 return;
             }
-            boolean b = expected.equals(actual);
 
+            // Compare byte-for-byte
+            boolean b = Bytes.compare(expected, actual) == 0;
             if ( !b ) {
                 System.out.println("**** Test: "+manifestEntry.getName());
                 System.out.println("---- Input");
-                String inputString = readFile(input);
+                byte[] bytes = readFile(input);
+                String inputString = Bytes.bytes2string(actual);
                 System.out.print(inputString);
                 System.out.println("---- Actual");
-                System.out.print(actual);
+                System.out.print(Bytes.bytes2string(actual));
                 System.out.println("---- Expected");
-                System.out.print(expected);
+                System.out.print(Bytes.bytes2string(expected));
                 System.out.println("--------");
             }
             assertTrue(b, "Does not match expected canonical text");
         } catch (RiotException ex) {
-            if ( positiveTest )
-                throw ex;
+            if ( positiveTest ) {
+                fail(ex.getMessage());
+                //throw ex;
+            }
         }
     }
 
-    public void run4()
-    {
-        DatasetGraph dsg = DatasetGraphFactory.create();
+    public void run4() {
+        DatasetGraph dsg = new DatasetGraphOrdered();
         StreamRDF dest = StreamRDFLib.dataset(dsg);
         try {
-            parser.accept(dest);
+            
RDFParser.create().errorHandler(ErrorHandlerFactory.errorHandlerIgnoreWarnings(null))
+            .strict(true).forceLang(lang).base(baseIRI).source(input)
+            // ******
+            .labelToNode(LabelToNode.createUseLabelAsGiven())
+            .parse(dest);
 
-            Lang outLang = RDFLanguages.filenameToLang(output, Lang.NQUADS);
+            Lang outLang = RDFLanguages.filenameToLang(output, Lang.NTRIPLES);
 
-            // Exactly this string.
-            String actual = 
RDFWriter.source(dsg).format(RDFFormat.NQUADS_UTF8).asString();
+            // Special writer. NQuadsWriter_C14N / NodeFormatter_C14N / 
EscapeStr_C14N
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            RDFWriter.source(dsg).format(RDFFormat.NQUADS_C14N).output(out);
 
-            String expected;
+            byte[] actual = out.toByteArray();
+            byte[] expected;
             try {
                 expected = readFile(output);
             } catch (RiotException ex) {
                 fail("Failed to read results: "+ex.getMessage());
                 return;
             }
-            boolean b = expected.equals(actual);
-
+            boolean b = Bytes.compare(expected, actual) == 0;
             if ( !b ) {
                 System.out.println("**** Test: "+manifestEntry.getName());
                 System.out.println("---- Input");
-                String inputString = readFile(input);
+                byte[] bytes = readFile(input);
+                String inputString = Bytes.bytes2string(actual);
                 System.out.print(inputString);
                 System.out.println("---- Actual");
-                System.out.print(actual);
+                System.out.print(Bytes.bytes2string(actual));
                 System.out.println("---- Expected");
-                System.out.print(expected);
+                System.out.print(Bytes.bytes2string(expected));
                 System.out.println("--------");
             }
             assertTrue(b, "Does not match expected canonical text");
@@ -150,9 +171,13 @@ public class RiotC14NTest extends AbstractManifestTest {
         }
     }
 
-    String readFile(String name) {
-        if ( name.startsWith("file:") )
-            return IO.readWholeFileAsUTF8(IRILib.IRIToFilename(name));
-        return IO.readWholeFileAsUTF8(name);
+    private byte[] readFile(String filename) {
+        String fn = ( filename.startsWith("file:")) ? 
IRILib.IRIToFilename(filename) : filename;
+        Path path = Path.of(fn);
+        try {
+            return Files.readAllBytes(path);
+        } catch (IOException ex) {
+            throw IOX.exception(ex);
+        }
     }
 }
diff --git 
a/jena-arq/src/test/java/org/apache/jena/arq/junit/riot/VocabLangRDF.java 
b/jena-arq/src/test/java/org/apache/jena/arq/junit/riot/VocabLangRDF.java
index 8bb6fadf3e..0704260fe6 100644
--- a/jena-arq/src/test/java/org/apache/jena/arq/junit/riot/VocabLangRDF.java
+++ b/jena-arq/src/test/java/org/apache/jena/arq/junit/riot/VocabLangRDF.java
@@ -35,9 +35,11 @@ public class VocabLangRDF
     /** <p>The namespace of the vocabulary as a string</p> */
     public static final String NS = "http://www.w3.org/ns/rdftest#";;
 
-    /** <p>The namespace of the vocabulary as a string</p>
-     *  @see #NS */
-    public static String getURI() {return NS;}
+    /**
+     * The namespace of the vocabulary as a string.
+     * @see #NS
+     */
+    public static String getURI() { return NS; }
 
     /** <p>The namespace of the vocabulary as a resource</p> */
     public static final Resource NAMESPACE = m_model.createResource( NS );
diff --git 
a/jena-arq/src/test/java/org/apache/jena/arq/junit/textrunner/TextTestRunner.java
 
b/jena-arq/src/test/java/org/apache/jena/arq/junit/textrunner/TextTestRunner.java
index 084ce7dee0..f02865a481 100644
--- 
a/jena-arq/src/test/java/org/apache/jena/arq/junit/textrunner/TextTestRunner.java
+++ 
b/jena-arq/src/test/java/org/apache/jena/arq/junit/textrunner/TextTestRunner.java
@@ -90,7 +90,7 @@ public class TextTestRunner {
 
         if ( produceEarlReport ) {
             // Build report, no output.
-            launcher.registerTestExecutionListeners(executionStats);
+            launcher.registerTestExecutionListeners(executionStats, 
summaryListener);
         } else {
             launcher.registerTestExecutionListeners(executionStats, 
printExecListener, summaryListener);
         }
@@ -98,11 +98,10 @@ public class TextTestRunner {
         // Run, which calls the TestFactory which generates the tests from the 
manifest.
         launcher.execute(request);
 
-        // For skips tests.
         TestExecutionSummary summary = summaryListener.getSummary();
 
         if ( produceEarlReport ) {
-            
//RDFWriter.source(earlReport.getModel()).format(RDFFormat.TURTLE).output(System.out);
+            // out.println("Tests skipped: "+summary.getTestsSkippedCount());
             EarlReporter.clearEarlReport();
         } else {
             out.println();
@@ -113,7 +112,6 @@ public class TextTestRunner {
                 out.printf("** Failures: %s\n", 
executionStats.getTestFailures());
                 out.println();
             }
-            // summary should be null only when producing EARL reports.
             //summary.printTo
             if ( summary.getTestsSkippedCount() > 0 ) {
                 out.println("Tests pass:    "+executionStats.getTestPasses());
diff --git a/jena-arq/src/test/java/org/apache/jena/riot/Scripts_c14n.java 
b/jena-arq/src/test/java/org/apache/jena/riot/Scripts_C14N.java
similarity index 89%
rename from jena-arq/src/test/java/org/apache/jena/riot/Scripts_c14n.java
rename to jena-arq/src/test/java/org/apache/jena/riot/Scripts_C14N.java
index f317906f9c..0688f6c2cf 100644
--- a/jena-arq/src/test/java/org/apache/jena/riot/Scripts_c14n.java
+++ b/jena-arq/src/test/java/org/apache/jena/riot/Scripts_C14N.java
@@ -23,7 +23,6 @@ package org.apache.jena.riot;
 
 import java.util.stream.Stream;
 
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.DisplayName;
 import org.junit.jupiter.api.DynamicNode;
 import org.junit.jupiter.api.TestFactory;
@@ -32,21 +31,20 @@ import org.apache.jena.arq.TestConsts;
 import org.apache.jena.arq.junit.Scripts;
 
 /** Canonicalization tests - N-triples and N-Quads */
-@Disabled
-public class Scripts_c14n {
+public class Scripts_C14N {
 
     //static final String DIR="testing/rdf-tests-cg/";
 
     // Canonicalization tests
 
     @TestFactory
-    @DisplayName("rdf-tests CG - N-Quads Canonicalization")
+    @DisplayName("rdf-tests - N-Quads Canonicalization")
     public Stream<DynamicNode> testFactory_n_quads_c14n() {
         return 
Scripts.manifestTestFactory(TestConsts.RDF12_TESTS_DIR+"rdf-n-quads/c14n/manifest.ttl");
     }
 
     @TestFactory
-    @DisplayName("rdf-tests CG - N-Triples Canonicalization")
+    @DisplayName("rdf-tests - N-Triples Canonicalization")
     public Stream<DynamicNode> testFactory_n_Triples_c14n() {
         return 
Scripts.manifestTestFactory(TestConsts.RDF12_TESTS_DIR+"rdf-n-triples/c14n/manifest.ttl");
     }
diff --git 
a/jena-arq/src/test/java/org/apache/jena/riot/Scripts_RIOT_rdf_tests_std.java 
b/jena-arq/src/test/java/org/apache/jena/riot/Scripts_RIOT_rdf_tests_std.java
index ee8817cdc3..cea736bd95 100644
--- 
a/jena-arq/src/test/java/org/apache/jena/riot/Scripts_RIOT_rdf_tests_std.java
+++ 
b/jena-arq/src/test/java/org/apache/jena/riot/Scripts_RIOT_rdf_tests_std.java
@@ -29,7 +29,7 @@ import org.apache.jena.arq.TestConsts;
 import org.apache.jena.arq.junit.Scripts;
 
 /** Run the RDF Test CG test suites for RDF syntaxes
- * @see Scripts_c14n
+ * @see Scripts_C14N
  */
 @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
 public class Scripts_RIOT_rdf_tests_std {
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/Bytes.java 
b/jena-base/src/main/java/org/apache/jena/atlas/lib/Bytes.java
index 3b7be36a33..6c1f841ff3 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/lib/Bytes.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/Bytes.java
@@ -50,40 +50,34 @@ public class Bytes
     }
 
     /** Compare two byte arrays which may be of different lengths */
-    public static int compare(byte[] x1, byte[] x2)
-    {
+    public static int compare(byte[] x1, byte[] x2) {
         int n = Math.min(x1.length, x2.length);
 
-        for ( int i = 0; i < n; i++ )
-        {
+        for ( int i = 0 ; i < n ; i++ ) {
             byte b1 = x1[i];
             byte b2 = x2[i];
             if ( b1 == b2 )
                 continue;
             // Treat as unsigned values in the bytes.
-            return (b1&0xFF) - (b2&0xFF);
+            return (b1 & 0xFF) - (b2 & 0xFF);
         }
 
         return x1.length - x2.length;
     }
 
-    public static int compareByte(byte b1, byte b2)
-    {
-        return (b1&0xFF) - (b2&0xFF);
+    public static int compareByte(byte b1, byte b2) {
+        return (b1 & 0xFF) - (b2 & 0xFF);
     }
 
-    public static byte[] copyOf(byte[] bytes)
-    {
+    public static byte[] copyOf(byte[] bytes) {
         return copyOf(bytes, 0, bytes.length);
     }
 
-    public static byte[] copyOf(byte[] bytes, int start)
-    {
-        return copyOf(bytes, start, bytes.length-start);
+    public static byte[] copyOf(byte[] bytes, int start) {
+        return copyOf(bytes, start, bytes.length - start);
     }
 
-    public static byte[] copyOf(byte[] bytes, int start, int length)
-    {
+    public static byte[] copyOf(byte[] bytes, int start, int length) {
         byte[] newByteArray = new byte[length];
         System.arraycopy(bytes, start, newByteArray, 0, length);
         return newByteArray;
@@ -129,8 +123,7 @@ public class Bytes
      * @param b Byte Array
      * @param idx Starting point of bytes
      */
-    public static final int getInt(byte[]b, int idx)
-    {
+    public static final int getInt(byte[]b, int idx) {
         return assembleInt(b[idx+0],
                            b[idx+1],
                            b[idx+2],
@@ -147,8 +140,7 @@ public class Bytes
      * @param b Byte Array
      * @param idx Starting point of bytes
      */
-    public static final long getLong(byte[]b, int idx)
-    {
+    public static final long getLong(byte[]b, int idx) {
         return assembleLong(b[idx+0],
                             b[idx+1],
                             b[idx+2],
@@ -157,7 +149,6 @@ public class Bytes
                             b[idx+5],
                             b[idx+6],
                             b[idx+7]);
-
     }
 
     /** Put an int into a byte array
@@ -172,8 +163,7 @@ public class Bytes
      * @param b byte array
      * @param idx starting point
      */
-    public static final void setInt(int x, byte[]b, int idx)
-    {
+    public static final void setInt(int x, byte[]b, int idx) {
 //        b[idx+0] = byte3(value);
 //        b[idx+1] = byte2(value);
 //        b[idx+2] = byte1(value);
@@ -182,10 +172,8 @@ public class Bytes
       b[idx+1] = (byte)((x >> 16)&0xFF);
       b[idx+2] = (byte)((x >>  8)&0xFF);
       b[idx+3] = (byte)(x &0xFF);
-
     }
 
-
     /** Put a long into a byte array
      * @param value The integer
      * @param b byte array
@@ -228,7 +216,7 @@ public class Bytes
     }
 
     /** Make a long order of args -- high to low */
-    static private Long assembleLong(byte b7, byte b6, byte b5, byte b4, byte 
b3, byte b2, byte b1, byte b0) {
+    static private long assembleLong(byte b7, byte b6, byte b5, byte b4, byte 
b3, byte b2, byte b1, byte b0) {
         return ((b7 & 0xFFL) << 56) |
                ((b6 & 0xFFL) << 48) |
                ((b5 & 0xFFL) << 40) |
@@ -239,10 +227,10 @@ public class Bytes
                ((b0 & 0xFFL) <<  0);
     }
 
-    private static byte byte3(int x) { return (byte)(x >> 24); }
-    private static byte byte2(int x) { return (byte)(x >> 16); }
-    private static byte byte1(int x) { return (byte)(x >>  8); }
-    private static byte byte0(int x) { return (byte)(x >>  0); }
+//    private static byte byte3(int x) { return (byte)(x >> 24); }
+//    private static byte byte2(int x) { return (byte)(x >> 16); }
+//    private static byte byte1(int x) { return (byte)(x >>  8); }
+//    private static byte byte0(int x) { return (byte)(x >>  0); }
 
     /** Return the UTF-8 bytes for a string */
     public static byte[] string2bytes(String x) {
@@ -337,23 +325,19 @@ public class Bytes
     }
 
     /** Return a hex string representing the byte. */
-    public static String asHex(byte b)
-    {
+    public static String asHex(byte b) {
         return asHexUC(b);
     }
 
-    public static String asHexUC(byte b)
-    {
+    public static String asHexUC(byte b) {
         return asHex(b, Chars.hexDigitsUC);
     }
 
-    public static String asHexLC(byte b)
-    {
+    public static String asHexLC(byte b) {
         return asHex(b, Chars.hexDigitsLC);
     }
 
-    private static String asHex(byte b, char[] hexDigits)
-    {
+    private static String asHex(byte b, char[] hexDigits) {
         int hi = (b & 0xF0) >> 4;
         int lo = b & 0xF;
         char[] chars = new char[2];
@@ -362,16 +346,14 @@ public class Bytes
         return new String(chars);
     }
 
-
-    public static int hexCharToInt(char c)
-    {
+    public static int hexCharToInt(char c) {
         if ( '0' <= c && c <= '9' )
-            return c-'0';
+            return c - '0';
         else if ( 'A' <= c && c <= 'F' )
-            return c-'A'+10;
+            return c - 'A' + 10;
         else if ( 'a' <= c && c <= 'f' )
-            return c-'a'+10;
+            return c - 'a' + 10;
         else
-            throw new IllegalArgumentException("Bad index char : "+c);
+            throw new IllegalArgumentException("Bad index char : " + c);
     }
 }

Reply via email to