You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2023/11/03 15:45:59 UTC

(commons-rdf) 03/04: Sort main members

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

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-rdf.git

commit 558ab5188a9a4eac536025cf5340aa1c01bf46f4
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Fri Nov 3 11:45:42 2023 -0400

    Sort main members
---
 .../commons/rdf/jena/ConversionException.java      |   8 +-
 .../java/org/apache/commons/rdf/jena/JenaRDF.java  | 960 ++++++++++-----------
 .../rdf/jena/experimental/JenaRDFParser.java       |  42 +-
 .../commons/rdf/jena/impl/AbstractQuadLike.java    |  56 +-
 .../commons/rdf/jena/impl/JenaDatasetImpl.java     |  90 +-
 .../rdf/jena/impl/JenaGeneralizedQuadLikeImpl.java |   8 +-
 .../commons/rdf/jena/impl/JenaGraphImpl.java       |  54 +-
 .../commons/rdf/jena/impl/JenaLiteralImpl.java     |   8 +-
 .../commons/rdf/jsonldjava/JsonLdBlankNode.java    |  22 +-
 .../commons/rdf/jsonldjava/JsonLdDataset.java      |  18 +-
 .../apache/commons/rdf/jsonldjava/JsonLdGraph.java |  18 +-
 .../commons/rdf/jsonldjava/JsonLdGraphLike.java    | 128 +--
 .../apache/commons/rdf/jsonldjava/JsonLdIRI.java   |  16 +-
 .../commons/rdf/jsonldjava/JsonLdLiteral.java      |  72 +-
 .../commons/rdf/jsonldjava/JsonLdQuadLike.java     |  18 +-
 .../apache/commons/rdf/jsonldjava/JsonLdRDF.java   |  96 +--
 .../apache/commons/rdf/jsonldjava/JsonLdTerm.java  |  20 +-
 .../commons/rdf/jsonldjava/JsonLdUnionGraph.java   |  38 +-
 .../rdf/jsonldjava/experimental/JsonLdParser.java  |  40 +-
 .../java/org/apache/commons/rdf/rdf4j/RDF4J.java   | 376 ++++----
 .../org/apache/commons/rdf/rdf4j/RDF4JDataset.java |  74 +-
 .../org/apache/commons/rdf/rdf4j/RDF4JGraph.java   |  64 +-
 .../rdf/rdf4j/experimental/RDF4JParser.java        | 164 ++--
 .../rdf4j/impl/AbstractRepositoryGraphLike.java    |  24 +-
 .../commons/rdf/rdf4j/impl/BlankNodeImpl.java      |  10 +-
 .../rdf/rdf4j/impl/ConvertedStatements.java        |  38 +-
 .../apache/commons/rdf/rdf4j/impl/LiteralImpl.java |   8 +-
 .../commons/rdf/rdf4j/impl/ModelGraphImpl.java     |  60 +-
 .../rdf/rdf4j/impl/RepositoryDatasetImpl.java      | 176 ++--
 .../rdf/rdf4j/impl/RepositoryGraphImpl.java        | 104 +--
 .../apache/commons/rdf/simple/BlankNodeImpl.java   |  40 +-
 .../commons/rdf/simple/DatasetGraphView.java       |  34 +-
 .../org/apache/commons/rdf/simple/DatasetImpl.java |  98 +--
 .../org/apache/commons/rdf/simple/GraphImpl.java   |  58 +-
 .../org/apache/commons/rdf/simple/IRIImpl.java     |  30 +-
 .../org/apache/commons/rdf/simple/LiteralImpl.java |  44 +-
 .../org/apache/commons/rdf/simple/QuadImpl.java    |  42 +-
 .../org/apache/commons/rdf/simple/SimpleRDF.java   |  20 +-
 .../org/apache/commons/rdf/simple/TripleImpl.java  |  32 +-
 .../java/org/apache/commons/rdf/simple/Types.java  |  80 +-
 .../rdf/simple/experimental/AbstractRDFParser.java | 586 ++++++-------
 .../rdf/simple/experimental/RDFParseException.java |  10 +-
 42 files changed, 1942 insertions(+), 1942 deletions(-)

diff --git a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/ConversionException.java b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/ConversionException.java
index 5e170574..c304f908 100644
--- a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/ConversionException.java
+++ b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/ConversionException.java
@@ -37,11 +37,11 @@ public class ConversionException extends RuntimeException {
         super(message);
     }
 
-    public ConversionException(final Throwable cause) {
-        super(cause);
-    }
-
     public ConversionException(final String message, final Throwable cause) {
         super(message, cause);
     }
+
+    public ConversionException(final Throwable cause) {
+        super(cause);
+    }
 }
diff --git a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/JenaRDF.java b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/JenaRDF.java
index d99925c1..4d118311 100644
--- a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/JenaRDF.java
+++ b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/JenaRDF.java
@@ -79,180 +79,42 @@ public final class JenaRDF implements RDF {
     private static final InternalJenaFactory INTERNAL_JENA_FACTORY = new InternalJenaFactory() {
     };
 
-    private final UUID salt;
-
-    /**
-     * Create a JenaRDF.
-     * <p>
-     * This constructor will use a randomly generated {@link UUID} as a salt for
-     * the purposes of {@link BlankNode} identity, see {@link #salt()}.
-     */
-    public JenaRDF() {
-        this.salt = UUID.randomUUID();
-    }
-
-    /**
-     * Create a JenaRDF.
-     * <p>
-     * This constructor will use the specified {@link UUID} as a salt for the
-     * purposes of {@link BlankNode} identity, and should only be used in cases
-     * where predictable and consistent {@link BlankNode#uniqueReference()} are
-     * important.
-     *
-     * @param salt
-     *            {@link UUID} to use as salt for {@link BlankNode} equality
-     */
-    public JenaRDF(final UUID salt) {
-        this.salt = salt;
-    }
-
-    @Override
-    public JenaBlankNode createBlankNode() {
-        return INTERNAL_JENA_FACTORY.createBlankNode(salt());
-    }
-
-    @Override
-    public JenaBlankNode createBlankNode(final String name) {
-        return INTERNAL_JENA_FACTORY.createBlankNode(name, salt());
-    }
-
-    @Override
-    public JenaDataset createDataset() {
-        return INTERNAL_JENA_FACTORY.createDataset(salt());
-    }
-
-    @Override
-    public JenaGraph createGraph() {
-        return INTERNAL_JENA_FACTORY.createGraph(salt());
-    }
-
-    @Override
-    public JenaIRI createIRI(final String iri) {
-        validateIRI(iri);
-        return INTERNAL_JENA_FACTORY.createIRI(iri);
-    }
-
-    @Override
-    public JenaLiteral createLiteral(final String lexicalForm) {
-        return INTERNAL_JENA_FACTORY.createLiteral(lexicalForm);
-    }
-
-    @Override
-    public JenaLiteral createLiteral(final String lexicalForm, final IRI dataType) {
-        return INTERNAL_JENA_FACTORY.createLiteralDT(lexicalForm, dataType.getIRIString());
-    }
-
-    @Override
-    public JenaLiteral createLiteral(final String lexicalForm, final String languageTag) {
-        validateLang(languageTag);
-        return INTERNAL_JENA_FACTORY.createLiteralLang(lexicalForm, languageTag);
-    }
-
-    @Override
-    public JenaTriple createTriple(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        return INTERNAL_JENA_FACTORY.createTriple(subject, predicate, object);
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * In addition to supporting a <code>graphName</code> of <code>null</code>
-     * for representing a triple in the <em>default graph</em>, this method also
-     * recognize a {@link JenaIRI} which {@link JenaRDFTerm#asJenaNode()}
-     * represent the default graph according to
-     * {@link org.apache.jena.sparql.core.Quad#isDefaultGraph(Node)}, in which
-     * case the returned JenaQuad will have a {@link Quad#getGraphName()} of
-     * {@link Optional#empty()} rather than the provided IRI.
-     *
-     */
-    @Override
-    public JenaQuad createQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object)
-            throws IllegalArgumentException, UnsupportedOperationException {
-        return INTERNAL_JENA_FACTORY.createQuad(subject, predicate, object, graphName);
-    }
-
-    /**
-     * Create a generalized Jena triple.
-     * <p>
-     * The <em>generalized triple</em> supports any {@link RDFTerm} as its
-     * {@link TripleLike#getSubject()} {@link TripleLike#getPredicate()} or
-     * {@link TripleLike#getObject()}.
-     *
-     * @see #createTriple(BlankNodeOrIRI, IRI, RDFTerm)
-     * @see #createGeneralizedQuad(RDFTerm, RDFTerm, RDFTerm, RDFTerm)
-     *
-     * @param subject
-     *            The subject of the statement
-     * @param predicate
-     *            The predicate of the statement
-     * @param object
-     *            The object of the statement
-     * @return Generalized {@link TripleLike}. Note that the generalized triple
-     *         does <strong>not</strong> implement {@link Triple#equals(Object)}
-     *         or {@link Triple#hashCode()}.
-     */
-    public JenaGeneralizedTripleLike createGeneralizedTriple(final RDFTerm subject, final RDFTerm predicate, final RDFTerm object) {
-        return INTERNAL_JENA_FACTORY.createGeneralizedTriple(subject, predicate, object);
-    }
-
     /**
-     * Create a generalized Jena quad.
-     * <p>
-     * The <em>generalized quad</em> supports any {@link RDFTerm} as its
-     * {@link QuadLike#getSubject()} {@link QuadLike#getPredicate()},
-     * {@link QuadLike#getObject()} or {@link QuadLike#getObject()}.
+     * Convert from Jena {@link org.apache.jena.sparql.core.Quad} to a Commons
+     * RDF {@link Quad}.
      * <p>
-     * In addition to supporting a <code>graphName</code> of <code>null</code>
-     * for representing a triple in the <em>default graph</em>, this method also
-     * recognize a {@link JenaIRI} which {@link JenaRDFTerm#asJenaNode()}
-     * represent the default graph according to
-     * {@link org.apache.jena.sparql.core.Quad#isDefaultGraph(Node)}, in which
-     * case the returned JenaQuad will have a {@link Quad#getGraphName()} of
-     * {@link Optional#empty()} rather than the provided IRI.
-     *
-     * @see #createQuad(BlankNodeOrIRI, BlankNodeOrIRI, IRI, RDFTerm)
-     * @see #createGeneralizedTriple(RDFTerm, RDFTerm, RDFTerm)
-     *
-     * @param subject
-     *            The subject of the statement
-     * @param predicate
-     *            The predicate of the statement
-     * @param object
-     *            The object of the statement
-     * @param graphName
-     *            The graph name of the statement
-     * @return Generalized {@link QuadLike}. Note that the generalized quad does
-     *         <strong>not</strong> implement {@link Quad#equals(Object)} or
-     *         {@link Quad#hashCode()}.
-     */
-    public JenaGeneralizedQuadLike createGeneralizedQuad(final RDFTerm subject, final RDFTerm predicate, final RDFTerm object,
-            final RDFTerm graphName) {
-        return INTERNAL_JENA_FACTORY.createGeneralizedQuad(subject, predicate, object, graphName);
-    }
-
-    /**
-     * Adapt an existing Jena Node to CommonsRDF {@link RDFTerm}.
+     * Note that if any of the quad's nodes {@link Node#isBlank()}, then the
+     * factory's {@link RDF#createBlankNode(String)} will be used, meaning that
+     * care should be taken if reusing an {@link RDF} instance for multiple
+     * conversion sessions.
      * <p>
-     * If {@link Node#isLiteral()}, then the returned value is a
-     * {@link Literal}. If {@link Node#isURI()}, the returned value is a IRI. If
-     * {$@link Node#isBlank()}, the returned value is a {@link BlankNode}, which
-     * will use a {@link UUID} salt from this {@link JenaRDF} instance in
-     * combination with {@link Node#getBlankNodeId()} for the purpose of its
-     * {@link BlankNode#uniqueReference()}.
+     * If the provided quad {@link org.apache.jena.sparql.core.Quad#isDefaultGraph()},
+     * the returned {@link JenaQuadLike} has a {@link JenaQuadLike#getGraphName()}
+     * of {@link Optional#empty()}.
      *
-     * @see #asRDFTerm(RDF, Node)
+     * @see #asQuad(org.apache.jena.sparql.core.Quad)
+     * @see #asGeneralizedQuad(org.apache.jena.sparql.core.Quad)
      *
-     * @param node
-     *            The Jena Node to adapt. It's {@link Node#isConcrete()} must be
-     *            <code>true</code>.
-     * @return Adapted {@link JenaRDFTerm}
+     * @param factory
+     *            {@link RDF} to use for creating the {@link Triple} and its
+     *            {@link RDFTerm}s.
+     * @param quad
+     *            Jena {@link org.apache.jena.sparql.core.Quad} to adapt
+     * @return Converted {@link Quad}
      * @throws ConversionException
-     *             If the {@link Node} can't be represented as an
-     *             {@link RDFTerm}, e.g. if the node is not concrete or
-     *             represents a variable in Jena.
+     *             if any of the quad's nodes are not concrete or the quad is a
+     *             generalized quad
      */
-    public JenaRDFTerm asRDFTerm(final Node node) throws ConversionException {
-        return INTERNAL_JENA_FACTORY.createRDFTerm(node, salt());
+    public static Quad asQuad(final RDF factory, final org.apache.jena.sparql.core.Quad quad) {
+        if (factory instanceof JenaRDF) {
+            // No need to convert, just wrap
+            return ((JenaRDF) factory).asQuad(quad);
+        }
+        final BlankNodeOrIRI graphName = (BlankNodeOrIRI) (asRDFTerm(factory, quad.getGraph()));
+        final BlankNodeOrIRI subject = (BlankNodeOrIRI) (asRDFTerm(factory, quad.getSubject()));
+        final IRI predicate = (IRI) (asRDFTerm(factory, quad.getPredicate()));
+        final RDFTerm object = asRDFTerm(factory, quad.getObject());
+        return factory.createQuad(graphName, subject, predicate, object);
     }
 
     /**
@@ -306,52 +168,142 @@ public final class JenaRDF implements RDF {
     }
 
     /**
-     * Adapt an existing Jena Triple to CommonsRDF {@link Triple}.
+     * Convert from Jena {@link org.apache.jena.graph.Triple} to a Commons RDF
+     * {@link Triple}.
      * <p>
-     * If the triple contains any {@link Node#isBlank()}, then any corresponding
-     * {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF}
-     * instance in combination with {@link Node#getBlankNodeId()} for the
-     * purpose of its {@link BlankNode#uniqueReference()}.
+     * Note that if any of the triple's nodes {@link Node#isBlank()}, then the
+     * factory's {@link RDF#createBlankNode(String)} will be used, meaning that
+     * care should be taken if reusing an {@link RDF} instance for multiple
+     * conversion sessions.
      *
-     * @see #asTriple(RDF, org.apache.jena.graph.Triple)
+     * @see #asTriple(org.apache.jena.graph.Triple)
      *
+     * @param factory
+     *            {@link RDF} to use for creating the {@link Triple} and its
+     *            {@link RDFTerm}s.
      * @param triple
-     *            Jena {@link org.apache.jena.graph.Triple} to adapt
-     * @return Adapted {@link JenaTriple}
+     *            Jena triple
+     * @return Converted triple
      * @throws ConversionException
      *             if any of the triple's nodes are not concrete or the triple
      *             is a generalized triple
      */
-    public JenaTriple asTriple(final org.apache.jena.graph.Triple triple) throws ConversionException {
-        return INTERNAL_JENA_FACTORY.createTriple(triple, salt());
+    public static Triple asTriple(final RDF factory, final org.apache.jena.graph.Triple triple) throws ConversionException {
+        if (factory instanceof JenaRDF) {
+            // No need to convert, just wrap
+            return ((JenaRDF) factory).asTriple(triple);
+        }
+        final BlankNodeOrIRI subject;
+        final IRI predicate;
+        try {
+            subject = (BlankNodeOrIRI) asRDFTerm(factory, triple.getSubject());
+            predicate = (IRI) asRDFTerm(factory, triple.getPredicate());
+        } catch (final ClassCastException ex) {
+            throw new ConversionException("Can't convert generalized triple: " + triple, ex);
+        }
+        final RDFTerm object = asRDFTerm(factory, triple.getObject());
+        return factory.createTriple(subject, predicate, object);
     }
 
     /**
-     * Adapt a generalized Jena {@link org.apache.jena.graph.Triple} to a
-     * CommonsRDF {@link TripleLike}.
-     * <p>
-     * The generalized triple supports any {@link RDFTerm} as its
-     * {@link TripleLike#getSubject()} {@link TripleLike#getPredicate()} or
-     * {@link TripleLike#getObject()}.
+     * Create a {@link StreamRDF} instance that inserts the converted
+     * {@link Quad}s. into a the provided {@link Consumer}.
      * <p>
-     * If the Jena triple contains any {@link Node#isBlank()}, then any
-     * corresponding {@link BlankNode} will use a {@link UUID} salt from this
-     * {@link JenaRDF} instance in combination with
-     * {@link Node#getBlankNodeId()} for the purpose of its
-     * {@link BlankNode#uniqueReference()}.
-     *
-     * @see #asTriple(RDF, org.apache.jena.graph.Triple)
+     * The returned {@link StreamRDF} can be used for instance with Jena's
+     * {@link RDFDataMgr#parse(StreamRDF, String)}.
      *
-     * @param triple
-     *            Jena triple
-     * @return Adapted {@link TripleLike}. Note that the generalized triple does
-     *         <strong>not</strong> implement {@link Triple#equals(Object)} or
-     *         {@link Triple#hashCode()}.
-     * @throws ConversionException
-     *             if any of the triple's nodes are not concrete
+     * @param factory
+     *            {@link RDF} to use for creating {@link RDFTerm}s and
+     *            {@link Quad}s.
+     * @param consumer
+     *            A {@link Consumer} of {@link Quad}s
+     * @return A {@link StreamRDF} that will stream converted quads to the
+     *         consumer
      */
-    public JenaTripleLike asGeneralizedTriple(final org.apache.jena.graph.Triple triple) throws ConversionException {
-        return INTERNAL_JENA_FACTORY.createGeneralizedTriple(triple, salt());
+    public static StreamRDF streamJenaToQuad(final RDF factory, final Consumer<Quad> consumer) {
+        return new StreamRDFBase() {
+            @Override
+            public void quad(final org.apache.jena.sparql.core.Quad quad) {
+                consumer.accept(asQuad(factory, quad));
+            }
+        };
+    }
+
+    private static void validateLang(final String languageTag) {
+        if (languageTag.contains(" ")) {
+            throw new IllegalArgumentException("Invalid language tag: " + languageTag);
+        }
+    }
+
+    private final UUID salt;
+
+    /**
+     * Create a JenaRDF.
+     * <p>
+     * This constructor will use a randomly generated {@link UUID} as a salt for
+     * the purposes of {@link BlankNode} identity, see {@link #salt()}.
+     */
+    public JenaRDF() {
+        this.salt = UUID.randomUUID();
+    }
+
+    /**
+     * Create a JenaRDF.
+     * <p>
+     * This constructor will use the specified {@link UUID} as a salt for the
+     * purposes of {@link BlankNode} identity, and should only be used in cases
+     * where predictable and consistent {@link BlankNode#uniqueReference()} are
+     * important.
+     *
+     * @param salt
+     *            {@link UUID} to use as salt for {@link BlankNode} equality
+     */
+    public JenaRDF(final UUID salt) {
+        this.salt = salt;
+    }
+
+    /**
+     * Adapt an existing Jena {@link DatasetGraph} to CommonsRDF
+     * {@link Dataset}.
+     * <p>
+     * This does not take a copy, changes to the CommonsRDF Dataset are
+     * reflected in the jena dataset graph, which is accessible from
+     * {@link JenaDataset#asJenaDatasetGraph()}.
+     * <p>
+     * If the dataset contains any {@link Node#isBlank()}, then any
+     * corresponding {@link BlankNode} will use a {@link UUID} salt from this
+     * {@link JenaRDF} instance in combination with
+     * {@link Node#getBlankNodeId()} for the purpose of its
+     * {@link BlankNode#uniqueReference()}.
+     *
+     * @param datasetGraph
+     *            Jena {@link DatasetGraph} to adapt
+     * @return Adapted {@link JenaDataset}
+     */
+    public JenaDataset asDataset(final DatasetGraph datasetGraph) {
+        return INTERNAL_JENA_FACTORY.createDataset(datasetGraph, salt());
+    }
+
+    /**
+     * Adapt an existing Jena {@link org.apache.jena.query.Dataset} to
+     * CommonsRDF {@link Dataset}.
+     * <p>
+     * This does not take a copy, changes to the CommonsRDF Dataset are
+     * reflected in the jena dataset graph, which is accessible from
+     * {@link JenaDataset#asJenaDatasetGraph()}.
+     * <p>
+     * If the dataset contains any {@link Node#isBlank()}, then any
+     * corresponding {@link BlankNode} will use a {@link UUID} salt from this
+     * {@link JenaRDF} instance in combination with
+     * {@link Node#getBlankNodeId()} for the purpose of its
+     * {@link BlankNode#uniqueReference()}.
+     *
+     * @param datasetGraph
+     *            Jena {@link org.apache.jena.query.Dataset} to adapt
+     * @return Adapted {@link JenaDataset}
+     */
+    public JenaDataset asDataset(final org.apache.jena.query.Dataset datasetGraph) {
+        return INTERNAL_JENA_FACTORY.createDataset(datasetGraph.asDatasetGraph(), salt());
     }
 
     /**
@@ -388,62 +340,31 @@ public final class JenaRDF implements RDF {
     }
 
     /**
-     * Convert from Jena {@link org.apache.jena.graph.Triple} to a Commons RDF
-     * {@link Triple}.
+     * Adapt a generalized Jena {@link org.apache.jena.graph.Triple} to a
+     * CommonsRDF {@link TripleLike}.
      * <p>
-     * Note that if any of the triple's nodes {@link Node#isBlank()}, then the
-     * factory's {@link RDF#createBlankNode(String)} will be used, meaning that
-     * care should be taken if reusing an {@link RDF} instance for multiple
-     * conversion sessions.
+     * The generalized triple supports any {@link RDFTerm} as its
+     * {@link TripleLike#getSubject()} {@link TripleLike#getPredicate()} or
+     * {@link TripleLike#getObject()}.
+     * <p>
+     * If the Jena triple contains any {@link Node#isBlank()}, then any
+     * corresponding {@link BlankNode} will use a {@link UUID} salt from this
+     * {@link JenaRDF} instance in combination with
+     * {@link Node#getBlankNodeId()} for the purpose of its
+     * {@link BlankNode#uniqueReference()}.
      *
-     * @see #asTriple(org.apache.jena.graph.Triple)
+     * @see #asTriple(RDF, org.apache.jena.graph.Triple)
      *
-     * @param factory
-     *            {@link RDF} to use for creating the {@link Triple} and its
-     *            {@link RDFTerm}s.
      * @param triple
      *            Jena triple
-     * @return Converted triple
+     * @return Adapted {@link TripleLike}. Note that the generalized triple does
+     *         <strong>not</strong> implement {@link Triple#equals(Object)} or
+     *         {@link Triple#hashCode()}.
      * @throws ConversionException
-     *             if any of the triple's nodes are not concrete or the triple
-     *             is a generalized triple
-     */
-    public static Triple asTriple(final RDF factory, final org.apache.jena.graph.Triple triple) throws ConversionException {
-        if (factory instanceof JenaRDF) {
-            // No need to convert, just wrap
-            return ((JenaRDF) factory).asTriple(triple);
-        }
-        final BlankNodeOrIRI subject;
-        final IRI predicate;
-        try {
-            subject = (BlankNodeOrIRI) asRDFTerm(factory, triple.getSubject());
-            predicate = (IRI) asRDFTerm(factory, triple.getPredicate());
-        } catch (final ClassCastException ex) {
-            throw new ConversionException("Can't convert generalized triple: " + triple, ex);
-        }
-        final RDFTerm object = asRDFTerm(factory, triple.getObject());
-        return factory.createTriple(subject, predicate, object);
-    }
-
-    /**
-     * Adapt an existing Jena {@link org.apache.jena.sparql.core.Quad} to
-     * CommonsRDF {@link Quad}.
-     * <p>
-     * If the quad contains any {@link Node#isBlank()}, then any corresponding
-     * {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF}
-     * instance in combination with {@link Node#getBlankNodeId()} for the
-     * purpose of its {@link BlankNode#uniqueReference()}.
-     * <p>
-     * If the provided quad {@link org.apache.jena.sparql.core.Quad#isDefaultGraph()},
-     * the returned {@link JenaQuad} has a {@link Quad#getGraphName()}
-     * of {@link Optional#empty()}.
-     *
-     * @param quad
-     *            Jena quad
-     * @return Adapted quad
+     *             if any of the triple's nodes are not concrete
      */
-    public JenaQuad asQuad(final org.apache.jena.sparql.core.Quad quad) {
-        return INTERNAL_JENA_FACTORY.createQuad(quad, salt());
+    public JenaTripleLike asGeneralizedTriple(final org.apache.jena.graph.Triple triple) throws ConversionException {
+        return INTERNAL_JENA_FACTORY.createGeneralizedTriple(triple, salt());
     }
 
     /**
@@ -489,96 +410,54 @@ public final class JenaRDF implements RDF {
     }
 
     /**
-     * Adapt an existing Jena {@link DatasetGraph} to CommonsRDF
-     * {@link Dataset}.
-     * <p>
-     * This does not take a copy, changes to the CommonsRDF Dataset are
-     * reflected in the jena dataset graph, which is accessible from
-     * {@link JenaDataset#asJenaDatasetGraph()}.
-     * <p>
-     * If the dataset contains any {@link Node#isBlank()}, then any
-     * corresponding {@link BlankNode} will use a {@link UUID} salt from this
-     * {@link JenaRDF} instance in combination with
-     * {@link Node#getBlankNodeId()} for the purpose of its
-     * {@link BlankNode#uniqueReference()}.
+     * Convert a CommonsRDF Dataset to a Jena Dataset. If the Dataset was from Jena
+     * originally, return that original object wrapped else create a copy using Jena
+     * objects.
      *
-     * @param datasetGraph
-     *            Jena {@link DatasetGraph} to adapt
-     * @return Adapted {@link JenaDataset}
+     * @param dataset
+     *            Commons RDF {@link Dataset} to convert
+     * @return Converted Jena {@link org.apache.jena.query.Dataset}
      */
-    public JenaDataset asDataset(final DatasetGraph datasetGraph) {
-        return INTERNAL_JENA_FACTORY.createDataset(datasetGraph, salt());
+    public org.apache.jena.query.Dataset asJenaDataset(final Dataset dataset) {
+        return DatasetFactory.wrap(asJenaDatasetGraph(dataset));
     }
 
     /**
-     * Adapt an existing Jena {@link org.apache.jena.query.Dataset} to
-     * CommonsRDF {@link Dataset}.
-     * <p>
-     * This does not take a copy, changes to the CommonsRDF Dataset are
-     * reflected in the jena dataset graph, which is accessible from
-     * {@link JenaDataset#asJenaDatasetGraph()}.
-     * <p>
-     * If the dataset contains any {@link Node#isBlank()}, then any
-     * corresponding {@link BlankNode} will use a {@link UUID} salt from this
-     * {@link JenaRDF} instance in combination with
-     * {@link Node#getBlankNodeId()} for the purpose of its
-     * {@link BlankNode#uniqueReference()}.
+     * Convert a CommonsRDF Dataset to a Jena DatasetGraph. If the Dataset was from Jena
+     * originally, return that original object's underlying DatasetGraph else create a
+     * copy using Jena objects.
      *
-     * @param datasetGraph
-     *            Jena {@link org.apache.jena.query.Dataset} to adapt
-     * @return Adapted {@link JenaDataset}
+     * @param dataset
+     *            Commons RDF {@link Dataset} to convert
+     * @return Converted Jena {@link org.apache.jena.sparql.core.DatasetGraph}
      */
-    public JenaDataset asDataset(final org.apache.jena.query.Dataset datasetGraph) {
-        return INTERNAL_JENA_FACTORY.createDataset(datasetGraph.asDatasetGraph(), salt());
+    public DatasetGraph asJenaDatasetGraph(final Dataset dataset) {
+        final DatasetGraph dsg;
+        if (dataset instanceof JenaDataset) {
+            dsg = ((JenaDataset) dataset).asJenaDatasetGraph();
+        } else {
+            dsg = DatasetGraphFactory.createGeneral();
+            dataset.stream().map(this::asJenaQuad).forEach(dsg::add);
+        }
+        return dsg;
     }
 
     /**
-     * Convert from Jena {@link org.apache.jena.sparql.core.Quad} to a Commons
-     * RDF {@link Quad}.
-     * <p>
-     * Note that if any of the quad's nodes {@link Node#isBlank()}, then the
-     * factory's {@link RDF#createBlankNode(String)} will be used, meaning that
-     * care should be taken if reusing an {@link RDF} instance for multiple
-     * conversion sessions.
-     * <p>
-     * If the provided quad {@link org.apache.jena.sparql.core.Quad#isDefaultGraph()},
-     * the returned {@link JenaQuadLike} has a {@link JenaQuadLike#getGraphName()}
-     * of {@link Optional#empty()}.
-     *
-     * @see #asQuad(org.apache.jena.sparql.core.Quad)
-     * @see #asGeneralizedQuad(org.apache.jena.sparql.core.Quad)
+     * Convert a CommonsRDF Graph to a Jena Graph. If the Graph was from Jena
+     * originally, return that original object else create a copy using Jena
+     * objects.
      *
-     * @param factory
-     *            {@link RDF} to use for creating the {@link Triple} and its
-     *            {@link RDFTerm}s.
-     * @param quad
-     *            Jena {@link org.apache.jena.sparql.core.Quad} to adapt
-     * @return Converted {@link Quad}
-     * @throws ConversionException
-     *             if any of the quad's nodes are not concrete or the quad is a
-     *             generalized quad
+     * @param graph
+     *            Commons RDF {@link Graph} to convert
+     * @return Converted Jena {@link org.apache.jena.graph.Graph}
      */
-    public static Quad asQuad(final RDF factory, final org.apache.jena.sparql.core.Quad quad) {
-        if (factory instanceof JenaRDF) {
-            // No need to convert, just wrap
-            return ((JenaRDF) factory).asQuad(quad);
+    public org.apache.jena.graph.Graph asJenaGraph(final Graph graph) {
+        if (graph instanceof JenaGraph) {
+            return ((JenaGraph) graph).asJenaGraph();
         }
-        final BlankNodeOrIRI graphName = (BlankNodeOrIRI) (asRDFTerm(factory, quad.getGraph()));
-        final BlankNodeOrIRI subject = (BlankNodeOrIRI) (asRDFTerm(factory, quad.getSubject()));
-        final IRI predicate = (IRI) (asRDFTerm(factory, quad.getPredicate()));
-        final RDFTerm object = asRDFTerm(factory, quad.getObject());
-        return factory.createQuad(graphName, subject, predicate, object);
-    }
-
-    /**
-     * Return {@link RDFSyntax} corresponding to a Jena {@link Lang}.
-     *
-     * @param lang
-     *            {@link Lang} to convert
-     * @return Matched {@link RDFSyntax}, otherwise {@link Optional#empty()}
-     */
-    public Optional<RDFSyntax> asRDFSyntax(final Lang lang) {
-        return RDFSyntax.byMediaType(lang.getContentType().getContentType());
+        final org.apache.jena.graph.Graph g = GraphFactory.createGraphMem();
+        graph.stream().forEach(t -> g.add(asJenaTriple(t)));
+        return g;
     }
 
     /**
@@ -592,131 +471,6 @@ public final class JenaRDF implements RDF {
         return Optional.ofNullable(RDFLanguages.contentTypeToLang(rdfSyntax.mediaType()));
     }
 
-    /**
-     * Create a {@link StreamRDF} instance that inserts the converted
-     * {@link Quad}s. into a the provided {@link Consumer}.
-     * <p>
-     * The returned {@link StreamRDF} can be used for instance with Jena's
-     * {@link RDFDataMgr#parse(StreamRDF, String)}.
-     *
-     * @param factory
-     *            {@link RDF} to use for creating {@link RDFTerm}s and
-     *            {@link Quad}s.
-     * @param consumer
-     *            A {@link Consumer} of {@link Quad}s
-     * @return A {@link StreamRDF} that will stream converted quads to the
-     *         consumer
-     */
-    public static StreamRDF streamJenaToQuad(final RDF factory, final Consumer<Quad> consumer) {
-        return new StreamRDFBase() {
-            @Override
-            public void quad(final org.apache.jena.sparql.core.Quad quad) {
-                consumer.accept(asQuad(factory, quad));
-            }
-        };
-    }
-
-    /**
-     * Create a {@link StreamRDF} instance that inserts generalized
-     * {@link TripleLike}s. into a the provided {@link Consumer}.
-     * <p>
-     * A generalized triple allows any {@link RDFTerm} for
-     * {@link TripleLike#getSubject()}, {@link TripleLike#getPredicate()} and
-     * {@link TripleLike#getObject()}.
-     * <p>
-     * The returned {@link StreamRDF} can be used for instance with Jena's
-     * {@link RDFDataMgr#parse(StreamRDF, String)}.
-     *
-     * @param generalizedConsumer
-     *            A {@link Consumer} of generalized {@link TripleLike}s
-     * @return A {@link StreamRDF} that will stream generalized triples to the
-     *         consumer
-     */
-    public StreamRDF streamJenaToGeneralizedTriple(final Consumer<TripleLike> generalizedConsumer) {
-        return new StreamRDFBase() {
-            @Override
-            public void triple(final org.apache.jena.graph.Triple triple) {
-                generalizedConsumer.accept(asGeneralizedTriple(triple));
-            }
-        };
-    }
-
-    /**
-     * Create a {@link StreamRDF} instance that inserts generalized
-     * {@link QuadLike}s. into a the provided {@link Consumer}.
-     * <p>
-     * A generalized quad allows any {@link RDFTerm} for
-     * {@link QuadLike#getSubject()}, {@link TripleLike#getPredicate()},
-     * {@link QuadLike#getObject()} and {@link QuadLike#getGraphName()} .
-     * <p>
-     * The returned {@link StreamRDF} can be used for instance with Jena's
-     * {@link RDFDataMgr#parse(StreamRDF, String)}.
-     *
-     * @param generalizedConsumer
-     *            A {@link Consumer} of generalized {@link QuadLike}s
-     * @return A {@link StreamRDF} that will stream generalized quads to the
-     *         consumer
-     */
-    public StreamRDF streamJenaToGeneralizedQuad(final Consumer<QuadLike<RDFTerm>> generalizedConsumer) {
-        return new StreamRDFBase() {
-            @Override
-            public void quad(final org.apache.jena.sparql.core.Quad quad) {
-                generalizedConsumer.accept(asGeneralizedQuad(quad));
-            }
-        };
-    }
-
-    /**
-     * Convert a CommonsRDF Dataset to a Jena Dataset. If the Dataset was from Jena
-     * originally, return that original object wrapped else create a copy using Jena
-     * objects.
-     *
-     * @param dataset
-     *            Commons RDF {@link Dataset} to convert
-     * @return Converted Jena {@link org.apache.jena.query.Dataset}
-     */
-    public org.apache.jena.query.Dataset asJenaDataset(final Dataset dataset) {
-        return DatasetFactory.wrap(asJenaDatasetGraph(dataset));
-    }
-
-    /**
-     * Convert a CommonsRDF Dataset to a Jena DatasetGraph. If the Dataset was from Jena
-     * originally, return that original object's underlying DatasetGraph else create a
-     * copy using Jena objects.
-     *
-     * @param dataset
-     *            Commons RDF {@link Dataset} to convert
-     * @return Converted Jena {@link org.apache.jena.sparql.core.DatasetGraph}
-     */
-    public DatasetGraph asJenaDatasetGraph(final Dataset dataset) {
-        final DatasetGraph dsg;
-        if (dataset instanceof JenaDataset) {
-            dsg = ((JenaDataset) dataset).asJenaDatasetGraph();
-        } else {
-            dsg = DatasetGraphFactory.createGeneral();
-            dataset.stream().map(this::asJenaQuad).forEach(dsg::add);
-        }
-        return dsg;
-    }
-
-    /**
-     * Convert a CommonsRDF Graph to a Jena Graph. If the Graph was from Jena
-     * originally, return that original object else create a copy using Jena
-     * objects.
-     *
-     * @param graph
-     *            Commons RDF {@link Graph} to convert
-     * @return Converted Jena {@link org.apache.jena.graph.Graph}
-     */
-    public org.apache.jena.graph.Graph asJenaGraph(final Graph graph) {
-        if (graph instanceof JenaGraph) {
-            return ((JenaGraph) graph).asJenaGraph();
-        }
-        final org.apache.jena.graph.Graph g = GraphFactory.createGraphMem();
-        graph.stream().forEach(t -> g.add(asJenaTriple(t)));
-        return g;
-    }
-
     /**
      * Convert a CommonsRDF RDFTerm to a Jena Node. If the RDFTerm was from Jena
      * originally, return that original object, else create a copy using Jena
@@ -755,6 +509,28 @@ public final class JenaRDF implements RDF {
         throw new ConversionException("Not a concrete RDF Term: " + term);
     }
 
+    /**
+     * Convert a CommonsRDF {@link Quad} to a Jena
+     * {@link org.apache.jena.sparql.core.Quad}.
+     * <p>
+     * If the quad was from Jena originally, return that original object,
+     * otherwise create a copy using Jena objects.
+     *
+     * @param quad
+     *            Commons RDF {@link Quad} to convert
+     * @return Converted Jena {@link org.apache.jena.sparql.core.Quad}
+     */
+    public org.apache.jena.sparql.core.Quad asJenaQuad(final Quad quad) {
+        if (quad instanceof JenaQuad) {
+            return ((JenaQuad) quad).asJenaQuad();
+        }
+        return org.apache.jena.sparql.core.Quad.create(
+                asJenaNode(quad.getGraphName().orElse(null)),
+                asJenaNode(quad.getSubject()),
+                asJenaNode(quad.getPredicate()),
+                asJenaNode(quad.getObject()));
+    }
+
     /**
      * Convert a CommonsRDF {@link Triple} to a Jena
      * {@link org.apache.jena.graph.Triple}.
@@ -776,44 +552,205 @@ public final class JenaRDF implements RDF {
     }
 
     /**
-     * Convert a CommonsRDF {@link Quad} to a Jena
-     * {@link org.apache.jena.sparql.core.Quad}.
+     * Adapt an existing Jena {@link org.apache.jena.sparql.core.Quad} to
+     * CommonsRDF {@link Quad}.
      * <p>
-     * If the quad was from Jena originally, return that original object,
-     * otherwise create a copy using Jena objects.
+     * If the quad contains any {@link Node#isBlank()}, then any corresponding
+     * {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF}
+     * instance in combination with {@link Node#getBlankNodeId()} for the
+     * purpose of its {@link BlankNode#uniqueReference()}.
+     * <p>
+     * If the provided quad {@link org.apache.jena.sparql.core.Quad#isDefaultGraph()},
+     * the returned {@link JenaQuad} has a {@link Quad#getGraphName()}
+     * of {@link Optional#empty()}.
      *
      * @param quad
-     *            Commons RDF {@link Quad} to convert
-     * @return Converted Jena {@link org.apache.jena.sparql.core.Quad}
+     *            Jena quad
+     * @return Adapted quad
      */
-    public org.apache.jena.sparql.core.Quad asJenaQuad(final Quad quad) {
-        if (quad instanceof JenaQuad) {
-            return ((JenaQuad) quad).asJenaQuad();
-        }
-        return org.apache.jena.sparql.core.Quad.create(
-                asJenaNode(quad.getGraphName().orElse(null)),
-                asJenaNode(quad.getSubject()),
-                asJenaNode(quad.getPredicate()),
-                asJenaNode(quad.getObject()));
+    public JenaQuad asQuad(final org.apache.jena.sparql.core.Quad quad) {
+        return INTERNAL_JENA_FACTORY.createQuad(quad, salt());
     }
 
-    // Some simple validations - full IRI parsing is not cheap.
-    private void validateIRI(final String iri) {
-        if (iri.contains(" ")) {
-            throw new IllegalArgumentException();
-        }
-        if (iri.contains("<")) {
-            throw new IllegalArgumentException();
-        }
-        if (iri.contains(">")) {
-            throw new IllegalArgumentException();
-        }
+    /**
+     * Return {@link RDFSyntax} corresponding to a Jena {@link Lang}.
+     *
+     * @param lang
+     *            {@link Lang} to convert
+     * @return Matched {@link RDFSyntax}, otherwise {@link Optional#empty()}
+     */
+    public Optional<RDFSyntax> asRDFSyntax(final Lang lang) {
+        return RDFSyntax.byMediaType(lang.getContentType().getContentType());
     }
 
-    private static void validateLang(final String languageTag) {
-        if (languageTag.contains(" ")) {
-            throw new IllegalArgumentException("Invalid language tag: " + languageTag);
-        }
+    /**
+     * Adapt an existing Jena Node to CommonsRDF {@link RDFTerm}.
+     * <p>
+     * If {@link Node#isLiteral()}, then the returned value is a
+     * {@link Literal}. If {@link Node#isURI()}, the returned value is a IRI. If
+     * {$@link Node#isBlank()}, the returned value is a {@link BlankNode}, which
+     * will use a {@link UUID} salt from this {@link JenaRDF} instance in
+     * combination with {@link Node#getBlankNodeId()} for the purpose of its
+     * {@link BlankNode#uniqueReference()}.
+     *
+     * @see #asRDFTerm(RDF, Node)
+     *
+     * @param node
+     *            The Jena Node to adapt. It's {@link Node#isConcrete()} must be
+     *            <code>true</code>.
+     * @return Adapted {@link JenaRDFTerm}
+     * @throws ConversionException
+     *             If the {@link Node} can't be represented as an
+     *             {@link RDFTerm}, e.g. if the node is not concrete or
+     *             represents a variable in Jena.
+     */
+    public JenaRDFTerm asRDFTerm(final Node node) throws ConversionException {
+        return INTERNAL_JENA_FACTORY.createRDFTerm(node, salt());
+    }
+
+    /**
+     * Adapt an existing Jena Triple to CommonsRDF {@link Triple}.
+     * <p>
+     * If the triple contains any {@link Node#isBlank()}, then any corresponding
+     * {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF}
+     * instance in combination with {@link Node#getBlankNodeId()} for the
+     * purpose of its {@link BlankNode#uniqueReference()}.
+     *
+     * @see #asTriple(RDF, org.apache.jena.graph.Triple)
+     *
+     * @param triple
+     *            Jena {@link org.apache.jena.graph.Triple} to adapt
+     * @return Adapted {@link JenaTriple}
+     * @throws ConversionException
+     *             if any of the triple's nodes are not concrete or the triple
+     *             is a generalized triple
+     */
+    public JenaTriple asTriple(final org.apache.jena.graph.Triple triple) throws ConversionException {
+        return INTERNAL_JENA_FACTORY.createTriple(triple, salt());
+    }
+
+    @Override
+    public JenaBlankNode createBlankNode() {
+        return INTERNAL_JENA_FACTORY.createBlankNode(salt());
+    }
+
+    @Override
+    public JenaBlankNode createBlankNode(final String name) {
+        return INTERNAL_JENA_FACTORY.createBlankNode(name, salt());
+    }
+
+    @Override
+    public JenaDataset createDataset() {
+        return INTERNAL_JENA_FACTORY.createDataset(salt());
+    }
+
+    /**
+     * Create a generalized Jena quad.
+     * <p>
+     * The <em>generalized quad</em> supports any {@link RDFTerm} as its
+     * {@link QuadLike#getSubject()} {@link QuadLike#getPredicate()},
+     * {@link QuadLike#getObject()} or {@link QuadLike#getObject()}.
+     * <p>
+     * In addition to supporting a <code>graphName</code> of <code>null</code>
+     * for representing a triple in the <em>default graph</em>, this method also
+     * recognize a {@link JenaIRI} which {@link JenaRDFTerm#asJenaNode()}
+     * represent the default graph according to
+     * {@link org.apache.jena.sparql.core.Quad#isDefaultGraph(Node)}, in which
+     * case the returned JenaQuad will have a {@link Quad#getGraphName()} of
+     * {@link Optional#empty()} rather than the provided IRI.
+     *
+     * @see #createQuad(BlankNodeOrIRI, BlankNodeOrIRI, IRI, RDFTerm)
+     * @see #createGeneralizedTriple(RDFTerm, RDFTerm, RDFTerm)
+     *
+     * @param subject
+     *            The subject of the statement
+     * @param predicate
+     *            The predicate of the statement
+     * @param object
+     *            The object of the statement
+     * @param graphName
+     *            The graph name of the statement
+     * @return Generalized {@link QuadLike}. Note that the generalized quad does
+     *         <strong>not</strong> implement {@link Quad#equals(Object)} or
+     *         {@link Quad#hashCode()}.
+     */
+    public JenaGeneralizedQuadLike createGeneralizedQuad(final RDFTerm subject, final RDFTerm predicate, final RDFTerm object,
+            final RDFTerm graphName) {
+        return INTERNAL_JENA_FACTORY.createGeneralizedQuad(subject, predicate, object, graphName);
+    }
+
+    /**
+     * Create a generalized Jena triple.
+     * <p>
+     * The <em>generalized triple</em> supports any {@link RDFTerm} as its
+     * {@link TripleLike#getSubject()} {@link TripleLike#getPredicate()} or
+     * {@link TripleLike#getObject()}.
+     *
+     * @see #createTriple(BlankNodeOrIRI, IRI, RDFTerm)
+     * @see #createGeneralizedQuad(RDFTerm, RDFTerm, RDFTerm, RDFTerm)
+     *
+     * @param subject
+     *            The subject of the statement
+     * @param predicate
+     *            The predicate of the statement
+     * @param object
+     *            The object of the statement
+     * @return Generalized {@link TripleLike}. Note that the generalized triple
+     *         does <strong>not</strong> implement {@link Triple#equals(Object)}
+     *         or {@link Triple#hashCode()}.
+     */
+    public JenaGeneralizedTripleLike createGeneralizedTriple(final RDFTerm subject, final RDFTerm predicate, final RDFTerm object) {
+        return INTERNAL_JENA_FACTORY.createGeneralizedTriple(subject, predicate, object);
+    }
+
+    @Override
+    public JenaGraph createGraph() {
+        return INTERNAL_JENA_FACTORY.createGraph(salt());
+    }
+
+    @Override
+    public JenaIRI createIRI(final String iri) {
+        validateIRI(iri);
+        return INTERNAL_JENA_FACTORY.createIRI(iri);
+    }
+
+    @Override
+    public JenaLiteral createLiteral(final String lexicalForm) {
+        return INTERNAL_JENA_FACTORY.createLiteral(lexicalForm);
+    }
+
+    @Override
+    public JenaLiteral createLiteral(final String lexicalForm, final IRI dataType) {
+        return INTERNAL_JENA_FACTORY.createLiteralDT(lexicalForm, dataType.getIRIString());
+    }
+
+    @Override
+    public JenaLiteral createLiteral(final String lexicalForm, final String languageTag) {
+        validateLang(languageTag);
+        return INTERNAL_JENA_FACTORY.createLiteralLang(lexicalForm, languageTag);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * In addition to supporting a <code>graphName</code> of <code>null</code>
+     * for representing a triple in the <em>default graph</em>, this method also
+     * recognize a {@link JenaIRI} which {@link JenaRDFTerm#asJenaNode()}
+     * represent the default graph according to
+     * {@link org.apache.jena.sparql.core.Quad#isDefaultGraph(Node)}, in which
+     * case the returned JenaQuad will have a {@link Quad#getGraphName()} of
+     * {@link Optional#empty()} rather than the provided IRI.
+     *
+     */
+    @Override
+    public JenaQuad createQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object)
+            throws IllegalArgumentException, UnsupportedOperationException {
+        return INTERNAL_JENA_FACTORY.createQuad(subject, predicate, object, graphName);
+    }
+
+    @Override
+    public JenaTriple createTriple(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        return INTERNAL_JENA_FACTORY.createTriple(subject, predicate, object);
     }
 
     /**
@@ -831,4 +768,67 @@ public final class JenaRDF implements RDF {
         return salt;
     }
 
+    /**
+     * Create a {@link StreamRDF} instance that inserts generalized
+     * {@link QuadLike}s. into a the provided {@link Consumer}.
+     * <p>
+     * A generalized quad allows any {@link RDFTerm} for
+     * {@link QuadLike#getSubject()}, {@link TripleLike#getPredicate()},
+     * {@link QuadLike#getObject()} and {@link QuadLike#getGraphName()} .
+     * <p>
+     * The returned {@link StreamRDF} can be used for instance with Jena's
+     * {@link RDFDataMgr#parse(StreamRDF, String)}.
+     *
+     * @param generalizedConsumer
+     *            A {@link Consumer} of generalized {@link QuadLike}s
+     * @return A {@link StreamRDF} that will stream generalized quads to the
+     *         consumer
+     */
+    public StreamRDF streamJenaToGeneralizedQuad(final Consumer<QuadLike<RDFTerm>> generalizedConsumer) {
+        return new StreamRDFBase() {
+            @Override
+            public void quad(final org.apache.jena.sparql.core.Quad quad) {
+                generalizedConsumer.accept(asGeneralizedQuad(quad));
+            }
+        };
+    }
+
+    /**
+     * Create a {@link StreamRDF} instance that inserts generalized
+     * {@link TripleLike}s. into a the provided {@link Consumer}.
+     * <p>
+     * A generalized triple allows any {@link RDFTerm} for
+     * {@link TripleLike#getSubject()}, {@link TripleLike#getPredicate()} and
+     * {@link TripleLike#getObject()}.
+     * <p>
+     * The returned {@link StreamRDF} can be used for instance with Jena's
+     * {@link RDFDataMgr#parse(StreamRDF, String)}.
+     *
+     * @param generalizedConsumer
+     *            A {@link Consumer} of generalized {@link TripleLike}s
+     * @return A {@link StreamRDF} that will stream generalized triples to the
+     *         consumer
+     */
+    public StreamRDF streamJenaToGeneralizedTriple(final Consumer<TripleLike> generalizedConsumer) {
+        return new StreamRDFBase() {
+            @Override
+            public void triple(final org.apache.jena.graph.Triple triple) {
+                generalizedConsumer.accept(asGeneralizedTriple(triple));
+            }
+        };
+    }
+
+    // Some simple validations - full IRI parsing is not cheap.
+    private void validateIRI(final String iri) {
+        if (iri.contains(" ")) {
+            throw new IllegalArgumentException();
+        }
+        if (iri.contains("<")) {
+            throw new IllegalArgumentException();
+        }
+        if (iri.contains(">")) {
+            throw new IllegalArgumentException();
+        }
+    }
+
 }
diff --git a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/experimental/JenaRDFParser.java b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/experimental/JenaRDFParser.java
index 2513e823..16f361d8 100644
--- a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/experimental/JenaRDFParser.java
+++ b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/experimental/JenaRDFParser.java
@@ -47,25 +47,8 @@ public class JenaRDFParser extends AbstractRDFParser<JenaRDFParser> {
         return new JenaRDF();
     }
 
-    public JenaRDFParser targetGeneralizedTriple(final Consumer<TripleLike> consumer) {
-        final JenaRDFParser c = this.clone();
-        c.resetTarget();
-        c.generalizedConsumerTriple = consumer;
-        return c;
-    }
-
-    public JenaRDFParser targetGeneralizedQuad(final Consumer<QuadLike<RDFTerm>> consumer) {
-        final JenaRDFParser c = this.clone();
-        c.resetTarget();
-        c.generalizedConsumerQuad = consumer;
-        return c;
-    }
-
-    @Override
-    protected void resetTarget() {
-        super.resetTarget();
-        this.generalizedConsumerTriple = null;
-        this.generalizedConsumerQuad = null;
+    private JenaRDF getJenaFactory() {
+        return (JenaRDF) getRdfTermFactory().filter(JenaRDF.class::isInstance).orElseGet(this::createRDFTermFactory);
     }
 
     @Override
@@ -99,8 +82,25 @@ public class JenaRDFParser extends AbstractRDFParser<JenaRDFParser> {
         }
     }
 
-    private JenaRDF getJenaFactory() {
-        return (JenaRDF) getRdfTermFactory().filter(JenaRDF.class::isInstance).orElseGet(this::createRDFTermFactory);
+    @Override
+    protected void resetTarget() {
+        super.resetTarget();
+        this.generalizedConsumerTriple = null;
+        this.generalizedConsumerQuad = null;
+    }
+
+    public JenaRDFParser targetGeneralizedQuad(final Consumer<QuadLike<RDFTerm>> consumer) {
+        final JenaRDFParser c = this.clone();
+        c.resetTarget();
+        c.generalizedConsumerQuad = consumer;
+        return c;
+    }
+
+    public JenaRDFParser targetGeneralizedTriple(final Consumer<TripleLike> consumer) {
+        final JenaRDFParser c = this.clone();
+        c.resetTarget();
+        c.generalizedConsumerTriple = consumer;
+        return c;
     }
 
 }
diff --git a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/AbstractQuadLike.java b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/AbstractQuadLike.java
index 4afe766b..37979d63 100644
--- a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/AbstractQuadLike.java
+++ b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/AbstractQuadLike.java
@@ -49,9 +49,6 @@ import org.apache.jena.sparql.core.Quad;
 abstract class AbstractQuadLike<S extends RDFTerm, P extends RDFTerm, O extends RDFTerm, G extends RDFTerm>
         implements JenaQuadLike<G> {
 
-    private static final InternalJenaFactory INTERNAL_JENA_FACTORY = new InternalJenaFactory() {
-    };
-
     /**
      * COMMONSRDF-55 - special handling of urn:x-arq:DefaultGraph and friends
      * <p>
@@ -98,6 +95,9 @@ abstract class AbstractQuadLike<S extends RDFTerm, P extends RDFTerm, O extends
         }
     }
 
+    private static final InternalJenaFactory INTERNAL_JENA_FACTORY = new InternalJenaFactory() {
+    };
+
     private static final DefaultGraphChecker DEFAULT_GRAPH_CHECKER = new DefaultGraphChecker();
 
     final Optional<G> graphName;
@@ -108,16 +108,13 @@ abstract class AbstractQuadLike<S extends RDFTerm, P extends RDFTerm, O extends
     org.apache.jena.graph.Triple triple = null;
 
 
-    AbstractQuadLike(final S subject, final P predicate, final O object, final Optional<G> graphName) {
-        this.subject = Objects.requireNonNull(subject);
-        this.predicate = Objects.requireNonNull(predicate);
-        this.object = Objects.requireNonNull(object);
-        // Enforce
-        this.graphName = Objects.requireNonNull(graphName).filter(DEFAULT_GRAPH_CHECKER::isNotDefaultGraphJenaNode);
-    }
-
-    AbstractQuadLike(final S subject, final P predicate, final O object) {
-        this(subject, predicate, object, Optional.empty());
+    @SuppressWarnings("unchecked")
+    AbstractQuadLike(final org.apache.jena.graph.Triple triple, final UUID salt) {
+        this.triple = Objects.requireNonNull(triple);
+        this.subject = (S) INTERNAL_JENA_FACTORY.createRDFTerm(triple.getSubject(), salt);
+        this.predicate = (P) INTERNAL_JENA_FACTORY.createRDFTerm(triple.getPredicate(), salt);
+        this.object = (O) INTERNAL_JENA_FACTORY.createRDFTerm(triple.getObject(), salt);
+        this.graphName = Optional.empty();
     }
 
     @SuppressWarnings("unchecked")
@@ -133,13 +130,16 @@ abstract class AbstractQuadLike<S extends RDFTerm, P extends RDFTerm, O extends
         }
     }
 
-    @SuppressWarnings("unchecked")
-    AbstractQuadLike(final org.apache.jena.graph.Triple triple, final UUID salt) {
-        this.triple = Objects.requireNonNull(triple);
-        this.subject = (S) INTERNAL_JENA_FACTORY.createRDFTerm(triple.getSubject(), salt);
-        this.predicate = (P) INTERNAL_JENA_FACTORY.createRDFTerm(triple.getPredicate(), salt);
-        this.object = (O) INTERNAL_JENA_FACTORY.createRDFTerm(triple.getObject(), salt);
-        this.graphName = Optional.empty();
+    AbstractQuadLike(final S subject, final P predicate, final O object) {
+        this(subject, predicate, object, Optional.empty());
+    }
+
+    AbstractQuadLike(final S subject, final P predicate, final O object, final Optional<G> graphName) {
+        this.subject = Objects.requireNonNull(subject);
+        this.predicate = Objects.requireNonNull(predicate);
+        this.object = Objects.requireNonNull(object);
+        // Enforce
+        this.graphName = Objects.requireNonNull(graphName).filter(DEFAULT_GRAPH_CHECKER::isNotDefaultGraphJenaNode);
     }
 
     @Override
@@ -168,23 +168,23 @@ abstract class AbstractQuadLike<S extends RDFTerm, P extends RDFTerm, O extends
     }
 
     @Override
-    public S getSubject() {
-        return subject;
+    public Optional<G> getGraphName() {
+        return graphName;
     }
 
     @Override
-    public P getPredicate() {
-        return predicate;
+    public O getObject() {
+        return object;
     }
 
     @Override
-    public O getObject() {
-        return object;
+    public P getPredicate() {
+        return predicate;
     }
 
     @Override
-    public Optional<G> getGraphName() {
-        return graphName;
+    public S getSubject() {
+        return subject;
     }
 
     @Override
diff --git a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaDatasetImpl.java b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaDatasetImpl.java
index 66b41147..6389f704 100644
--- a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaDatasetImpl.java
+++ b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaDatasetImpl.java
@@ -85,28 +85,39 @@ class JenaDatasetImpl implements JenaDataset {
                 toJenaPattern(object));
     }
 
-    private Node toJenaPattern(final Optional<? extends RDFTerm> graphName) {
-        // In theory we could have done:
-        // factory.toJena(graphName.orElse(internalJenaFactory::createAnyVariable))
-        // but because of generics casting rules that doesn't work :(
+    @Override
+    public boolean contains(final Quad quad) {
+        return datasetGraph.contains(factory.asJenaQuad(quad));
+    }
 
-        if (graphName == null) {
-            return ANY;
-        }
-        // null: default datasetGraph
-        return factory.asJenaNode(graphName.orElse(null));
+    @Override
+    public Graph getGraph() {
+        final GraphView g = GraphView.createDefaultGraph(datasetGraph);
+        return new JenaGraphImpl(g, salt);
     }
 
-    private Node toJenaPattern(final RDFTerm term) {
-        if (term == null) {
-            return ANY;
-        }
-        return factory.asJenaNode(term);
+    @Override
+    public Optional<Graph> getGraph(final BlankNodeOrIRI graphName) {
+        final GraphView gv = GraphView.createNamedGraph(datasetGraph, factory.asJenaNode(graphName));
+        return Optional.of(new JenaGraphImpl(gv, salt));
     }
 
     @Override
-    public boolean contains(final Quad quad) {
-        return datasetGraph.contains(factory.asJenaQuad(quad));
+    public Stream<BlankNodeOrIRI> getGraphNames() {
+        final JenaRDF factory = new JenaRDF(salt);
+        return Iter.asStream(datasetGraph.listGraphNodes()).map(node -> (BlankNodeOrIRI) factory.asRDFTerm(node));
+    }
+
+    @Override
+    public JenaGraph getUnionGraph() {
+        final GraphView gv = GraphView.createUnionGraph(datasetGraph);
+        return new JenaGraphImpl(gv, salt);
+    }
+
+    @Override
+    public Iterable<Quad> iterate() {
+        final JenaRDF factory = new JenaRDF(salt);
+        return Iter.asStream(datasetGraph.find(), false).map(q -> (Quad) factory.asQuad(q))::iterator;
     }
 
     @Override
@@ -146,41 +157,30 @@ class JenaDatasetImpl implements JenaDataset {
                 .map(factory::asQuad);
     }
 
-    @Override
-    public String toString() {
-        final StringWriter sw = new StringWriter();
-        RDFDataMgr.write(sw, datasetGraph, Lang.NQUADS);
-        return sw.toString();
-    }
-
-    @Override
-    public Graph getGraph() {
-        final GraphView g = GraphView.createDefaultGraph(datasetGraph);
-        return new JenaGraphImpl(g, salt);
-    }
-
-    @Override
-    public JenaGraph getUnionGraph() {
-        final GraphView gv = GraphView.createUnionGraph(datasetGraph);
-        return new JenaGraphImpl(gv, salt);
-    }
+    private Node toJenaPattern(final Optional<? extends RDFTerm> graphName) {
+        // In theory we could have done:
+        // factory.toJena(graphName.orElse(internalJenaFactory::createAnyVariable))
+        // but because of generics casting rules that doesn't work :(
 
-    @Override
-    public Optional<Graph> getGraph(final BlankNodeOrIRI graphName) {
-        final GraphView gv = GraphView.createNamedGraph(datasetGraph, factory.asJenaNode(graphName));
-        return Optional.of(new JenaGraphImpl(gv, salt));
+        if (graphName == null) {
+            return ANY;
+        }
+        // null: default datasetGraph
+        return factory.asJenaNode(graphName.orElse(null));
     }
 
-    @Override
-    public Stream<BlankNodeOrIRI> getGraphNames() {
-        final JenaRDF factory = new JenaRDF(salt);
-        return Iter.asStream(datasetGraph.listGraphNodes()).map(node -> (BlankNodeOrIRI) factory.asRDFTerm(node));
+    private Node toJenaPattern(final RDFTerm term) {
+        if (term == null) {
+            return ANY;
+        }
+        return factory.asJenaNode(term);
     }
 
     @Override
-    public Iterable<Quad> iterate() {
-        final JenaRDF factory = new JenaRDF(salt);
-        return Iter.asStream(datasetGraph.find(), false).map(q -> (Quad) factory.asQuad(q))::iterator;
+    public String toString() {
+        final StringWriter sw = new StringWriter();
+        RDFDataMgr.write(sw, datasetGraph, Lang.NQUADS);
+        return sw.toString();
     }
 
 }
diff --git a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaGeneralizedQuadLikeImpl.java b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaGeneralizedQuadLikeImpl.java
index cff48f3a..9a9abc86 100644
--- a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaGeneralizedQuadLikeImpl.java
+++ b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaGeneralizedQuadLikeImpl.java
@@ -27,12 +27,12 @@ import org.apache.jena.sparql.core.Quad;
 class JenaGeneralizedQuadLikeImpl extends AbstractQuadLike<RDFTerm, RDFTerm, RDFTerm, RDFTerm>
         implements JenaGeneralizedQuadLike {
 
-    JenaGeneralizedQuadLikeImpl(final RDFTerm subject, final RDFTerm predicate, final RDFTerm object, final Optional<RDFTerm> ofNullable) {
-        super(subject, predicate, object, ofNullable);
-    }
-
     JenaGeneralizedQuadLikeImpl(final Quad quad, final UUID salt) {
         super(quad, salt);
     }
 
+    JenaGeneralizedQuadLikeImpl(final RDFTerm subject, final RDFTerm predicate, final RDFTerm object, final Optional<RDFTerm> ofNullable) {
+        super(subject, predicate, object, ofNullable);
+    }
+
 }
diff --git a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaGraphImpl.java b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaGraphImpl.java
index 90e98815..86c9c6fc 100644
--- a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaGraphImpl.java
+++ b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaGraphImpl.java
@@ -43,15 +43,15 @@ class JenaGraphImpl implements JenaGraph {
     private final transient JenaRDF factory;
     private Model model;
 
-    JenaGraphImpl(final org.apache.jena.graph.Graph graph, final UUID salt) {
-        this.graph = graph;
+    JenaGraphImpl(final Model model, final UUID salt) {
+        this.model = model;
+        this.graph = model.getGraph();
         this.salt = salt;
         this.factory = new JenaRDF(salt);
     }
 
-    JenaGraphImpl(final Model model, final UUID salt) {
-        this.model = model;
-        this.graph = model.getGraph();
+    JenaGraphImpl(final org.apache.jena.graph.Graph graph, final UUID salt) {
+        this.graph = graph;
         this.salt = salt;
         this.factory = new JenaRDF(salt);
     }
@@ -72,6 +72,21 @@ class JenaGraphImpl implements JenaGraph {
         return graph;
     }
 
+    @Override
+    public Model asJenaModel() {
+        if (model == null) {
+            synchronized (this) {
+                // As Model can be used for locks, we should make sure we don't
+                // make
+                // more than one model
+                if (model == null) {
+                    model = ModelFactory.createModelForGraph(graph);
+                }
+            }
+        }
+        return model;
+    }
+
     @Override
     public void clear() {
         graph.clear();
@@ -98,13 +113,6 @@ class JenaGraphImpl implements JenaGraph {
                 toJenaPattern(object));
     }
 
-    private Node toJenaPattern(final RDFTerm pattern) {
-        if (pattern == null) {
-            return Node.ANY;
-        }
-        return factory.asJenaNode(pattern);
-    }
-
     @Override
     public void remove(final Triple triple) {
         if ((triple.getObject() instanceof Literal) &&
@@ -145,6 +153,13 @@ class JenaGraphImpl implements JenaGraph {
         return factory.asJenaNode(term);
     }
 
+    private Node toJenaPattern(final RDFTerm pattern) {
+        if (pattern == null) {
+            return Node.ANY;
+        }
+        return factory.asJenaNode(pattern);
+    }
+
     @Override
     public String toString() {
         final StringWriter sw = new StringWriter();
@@ -152,19 +167,4 @@ class JenaGraphImpl implements JenaGraph {
         return sw.toString();
     }
 
-    @Override
-    public Model asJenaModel() {
-        if (model == null) {
-            synchronized (this) {
-                // As Model can be used for locks, we should make sure we don't
-                // make
-                // more than one model
-                if (model == null) {
-                    model = ModelFactory.createModelForGraph(graph);
-                }
-            }
-        }
-        return model;
-    }
-
 }
diff --git a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaLiteralImpl.java b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaLiteralImpl.java
index 95df4dc1..69d54e40 100644
--- a/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaLiteralImpl.java
+++ b/commons-rdf-jena/src/main/java/org/apache/commons/rdf/jena/impl/JenaLiteralImpl.java
@@ -32,6 +32,10 @@ class JenaLiteralImpl extends AbstractJenaRDFTerm implements JenaLiteral {
     private static final InternalJenaFactory INTERNAL_JENA_FACTORY = new InternalJenaFactory() {
     };
 
+    private static String lowerCase(final String langTag) {
+        return langTag.toLowerCase(Locale.ROOT);
+    }
+
     JenaLiteralImpl(final Node node) {
         super(node);
         if (!node.isLiteral()) {
@@ -39,10 +43,6 @@ class JenaLiteralImpl extends AbstractJenaRDFTerm implements JenaLiteral {
         }
     }
 
-    private static String lowerCase(final String langTag) {
-        return langTag.toLowerCase(Locale.ROOT);
-    }
-
     @Override
     public boolean equals(final Object other) {
         if (other == this) {
diff --git a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdBlankNode.java b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdBlankNode.java
index d84f858d..3c4eb937 100644
--- a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdBlankNode.java
+++ b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdBlankNode.java
@@ -35,17 +35,6 @@ final class JsonLdBlankNodeImpl extends AbstractJsonLdTermImpl implements JsonLd
         }
     }
 
-    @Override
-    public String ntriplesString() {
-        // TODO: Escape if this is not valid ntriples string (e.g. contains :)
-        return node.getValue();
-    }
-
-    @Override
-    public String uniqueReference() {
-        return blankNodePrefix + node.getValue();
-    }
-
     @Override
     public boolean equals(final Object obj) {
         if (!(obj instanceof BlankNode)) {
@@ -60,8 +49,19 @@ final class JsonLdBlankNodeImpl extends AbstractJsonLdTermImpl implements JsonLd
         return uniqueReference().hashCode();
     }
 
+    @Override
+    public String ntriplesString() {
+        // TODO: Escape if this is not valid ntriples string (e.g. contains :)
+        return node.getValue();
+    }
+
     @Override
     public String toString() {
         return ntriplesString() + " [" + uniqueReference() + "]";
     }
+
+    @Override
+    public String uniqueReference() {
+        return blankNodePrefix + node.getValue();
+    }
 }
diff --git a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdDataset.java b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdDataset.java
index 4d4f168b..6243689f 100644
--- a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdDataset.java
+++ b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdDataset.java
@@ -54,6 +54,11 @@ class JsonLdDatasetImpl extends AbstractJsonLdGraphLike<org.apache.commons.rdf.a
         super.add(graphName, subject, predicate, object);
     }
 
+    @Override
+    Quad asTripleOrQuad(final com.github.jsonldjava.core.RDFDataset.Quad jsonldQuad) {
+        return factory.asQuad(jsonldQuad);
+    }
+
     @Override
     public boolean contains(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
         return super.contains(graphName, subject, predicate, object);
@@ -90,13 +95,6 @@ class JsonLdDatasetImpl extends AbstractJsonLdGraphLike<org.apache.commons.rdf.a
         remove(q.getGraphName(), q.getSubject(), q.getPredicate(), q.getObject());
     }
 
-    @Override
-    public Stream<? extends Quad> stream(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate,
-            final RDFTerm object) {
-        return filteredGraphs(graphName).flatMap(List::stream).filter(quadFilter(subject, predicate, object))
-                .map(factory::asQuad);
-    }
-
     @Override
     public long size() {
         return rdfDataSet.graphNames().stream().map(rdfDataSet::getQuads)
@@ -104,8 +102,10 @@ class JsonLdDatasetImpl extends AbstractJsonLdGraphLike<org.apache.commons.rdf.a
     }
 
     @Override
-    Quad asTripleOrQuad(final com.github.jsonldjava.core.RDFDataset.Quad jsonldQuad) {
-        return factory.asQuad(jsonldQuad);
+    public Stream<? extends Quad> stream(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate,
+            final RDFTerm object) {
+        return filteredGraphs(graphName).flatMap(List::stream).filter(quadFilter(subject, predicate, object))
+                .map(factory::asQuad);
     }
 
 }
diff --git a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdGraph.java b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdGraph.java
index 680ed37e..ad938c10 100644
--- a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdGraph.java
+++ b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdGraph.java
@@ -56,8 +56,8 @@ class JsonLdGraphImpl extends AbstractJsonLdGraphLike<Triple> implements JsonLdG
     }
 
     @Override
-    public void clear() {
-        filteredGraphs(graphName).forEach(List::clear);
+    public void add(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        super.add(graphName.orElse(null), subject, predicate, object);
     }
 
     @Override
@@ -67,8 +67,13 @@ class JsonLdGraphImpl extends AbstractJsonLdGraphLike<Triple> implements JsonLdG
     }
 
     @Override
-    public void add(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        super.add(graphName.orElse(null), subject, predicate, object);
+    JsonLdTriple asTripleOrQuad(final com.github.jsonldjava.core.RDFDataset.Quad jsonldQuad) {
+        return factory.asTriple(jsonldQuad);
+    }
+
+    @Override
+    public void clear() {
+        filteredGraphs(graphName).forEach(List::clear);
     }
 
     @Override
@@ -103,9 +108,4 @@ class JsonLdGraphImpl extends AbstractJsonLdGraphLike<Triple> implements JsonLdG
         return filteredGraphs(graphName).flatMap(List::stream).filter(quadFilter(subject, predicate, object))
                 .map(factory::asTriple);
     }
-
-    @Override
-    JsonLdTriple asTripleOrQuad(final com.github.jsonldjava.core.RDFDataset.Quad jsonldQuad) {
-        return factory.asTriple(jsonldQuad);
-    }
 }
diff --git a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdGraphLike.java b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdGraphLike.java
index ca307398..3a4abead 100644
--- a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdGraphLike.java
+++ b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdGraphLike.java
@@ -36,28 +36,6 @@ import org.apache.commons.rdf.api.TripleLike;
 import com.github.jsonldjava.core.RDFDataset;
 import com.github.jsonldjava.core.RDFDataset.Node;
 
-/**
- * Common abstract {@link GraphLike}.
- * <p>
- * Specialised by {@link JsonLdGraph}, {@link JsonLdUnionGraph} and
- * {@link JsonLdDataset}.
- *
- * @param <T>
- *            specialisation of {@link TripleLike}, e.g. {@link Triple} or
- *            {@link org.apache.commons.rdf.api.Quad}
- */
-public interface JsonLdGraphLike<T extends TripleLike> extends GraphLike<T> {
-    /**
-     * Return the underlying JSONLD-Java {@link RDFDataset}.
-     * <p>
-     * Changes in the JSONLD-Java dataset is reflected in this class and vice
-     * versa.
-     *
-     * @return The underlying JSONLD-JAva RDFDataset
-     */
-    RDFDataset getRdfDataSet();
-}
-
 abstract class AbstractJsonLdGraphLike<T extends TripleLike> implements JsonLdGraphLike<T> {
 
     /**
@@ -94,19 +72,6 @@ abstract class AbstractJsonLdGraphLike<T extends TripleLike> implements JsonLdGr
         this(new RDFDataset(), bnodePrefix);
     }
 
-    @Override
-    public void add(final T t) {
-        // add triples to default graph by default
-        BlankNodeOrIRI graphName = null;
-        if (t instanceof org.apache.commons.rdf.api.Quad) {
-            final org.apache.commons.rdf.api.Quad q = (org.apache.commons.rdf.api.Quad) t;
-            graphName = q.getGraphName().orElse(null);
-        }
-        // FIXME: JSON-LD's rdfDataSet.addQuad method does not support
-        // generalized RDF, so we have to do a naive cast here
-        add(graphName, (BlankNodeOrIRI) t.getSubject(), (IRI) t.getPredicate(), t.getObject());
-    }
-
     void add(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
         final String g = factory.asJsonLdString(graphName);
         final String s = factory.asJsonLdString(subject);
@@ -122,36 +87,17 @@ abstract class AbstractJsonLdGraphLike<T extends TripleLike> implements JsonLdGr
         }
     }
 
-    public void close() {
-        // Drop the memory reference, but don't clear it
-        rdfDataSet = null;
-    }
-
-    @Override
-    public void clear() {
-        filteredGraphs(null).forEach(List::clear);
-        // In theory we could use
-        // rdfDataSet.clear();
-        // but then we would need to also do
-        // rdfDataSet.put("@default", new ArrayList());
-        // .. both of which seems to be touching too much on JsonLd-Java's
-        // internal structure
-    }
-
-    @Override
-    public boolean contains(final T tripleOrQuad) {
-        return stream().anyMatch(Predicate.isEqual(tripleOrQuad));
-    }
-
     @Override
-    public RDFDataset getRdfDataSet() {
-        return rdfDataSet;
-    }
-
-    @Override
-    public Stream<? extends T> stream() {
-        return rdfDataSet.graphNames().parallelStream().map(rdfDataSet::getQuads)
-                .flatMap(List<RDFDataset.Quad>::parallelStream).map(this::asTripleOrQuad);
+    public void add(final T t) {
+        // add triples to default graph by default
+        BlankNodeOrIRI graphName = null;
+        if (t instanceof org.apache.commons.rdf.api.Quad) {
+            final org.apache.commons.rdf.api.Quad q = (org.apache.commons.rdf.api.Quad) t;
+            graphName = q.getGraphName().orElse(null);
+        }
+        // FIXME: JSON-LD's rdfDataSet.addQuad method does not support
+        // generalized RDF, so we have to do a naive cast here
+        add(graphName, (BlankNodeOrIRI) t.getSubject(), (IRI) t.getPredicate(), t.getObject());
     }
 
     /**
@@ -167,12 +113,33 @@ abstract class AbstractJsonLdGraphLike<T extends TripleLike> implements JsonLdGr
      */
     abstract T asTripleOrQuad(RDFDataset.Quad jsonldQuad);
 
+    @Override
+    public void clear() {
+        filteredGraphs(null).forEach(List::clear);
+        // In theory we could use
+        // rdfDataSet.clear();
+        // but then we would need to also do
+        // rdfDataSet.put("@default", new ArrayList());
+        // .. both of which seems to be touching too much on JsonLd-Java's
+        // internal structure
+    }
+
+    public void close() {
+        // Drop the memory reference, but don't clear it
+        rdfDataSet = null;
+    }
+
     // This will be made public in JsonLdDataset
     // and is used by the other methods.
     boolean contains(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI s, final IRI p, final RDFTerm o) {
         return filteredGraphs(graphName).flatMap(List::stream).anyMatch(quadFilter(s, p, o));
     }
 
+    @Override
+    public boolean contains(final T tripleOrQuad) {
+        return stream().anyMatch(Predicate.isEqual(tripleOrQuad));
+    }
+
     Stream<List<RDFDataset.Quad>> filteredGraphs(final Optional<BlankNodeOrIRI> graphName) {
         return rdfDataSet.graphNames().parallelStream()
                 // if graphName == null (wildcard), select all graphs,
@@ -184,6 +151,11 @@ abstract class AbstractJsonLdGraphLike<T extends TripleLike> implements JsonLdGr
                 .map(rdfDataSet::getQuads);
     }
 
+    @Override
+    public RDFDataset getRdfDataSet() {
+        return rdfDataSet;
+    }
+
     String graphNameAsJsonLdString(final T tripleOrQuad) {
         if (tripleOrQuad instanceof org.apache.commons.rdf.api.Quad) {
             final org.apache.commons.rdf.api.Quad quad = (org.apache.commons.rdf.api.Quad) tripleOrQuad;
@@ -233,4 +205,32 @@ abstract class AbstractJsonLdGraphLike<T extends TripleLike> implements JsonLdGr
         filteredGraphs(graphName).forEach(t -> t.removeIf(quadFilter(subject, predicate, object)));
     }
 
+    @Override
+    public Stream<? extends T> stream() {
+        return rdfDataSet.graphNames().parallelStream().map(rdfDataSet::getQuads)
+                .flatMap(List<RDFDataset.Quad>::parallelStream).map(this::asTripleOrQuad);
+    }
+
+}
+
+/**
+ * Common abstract {@link GraphLike}.
+ * <p>
+ * Specialised by {@link JsonLdGraph}, {@link JsonLdUnionGraph} and
+ * {@link JsonLdDataset}.
+ *
+ * @param <T>
+ *            specialisation of {@link TripleLike}, e.g. {@link Triple} or
+ *            {@link org.apache.commons.rdf.api.Quad}
+ */
+public interface JsonLdGraphLike<T extends TripleLike> extends GraphLike<T> {
+    /**
+     * Return the underlying JSONLD-Java {@link RDFDataset}.
+     * <p>
+     * Changes in the JSONLD-Java dataset is reflected in this class and vice
+     * versa.
+     *
+     * @return The underlying JSONLD-JAva RDFDataset
+     */
+    RDFDataset getRdfDataSet();
 }
diff --git a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdIRI.java b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdIRI.java
index a64ca87c..ac702b08 100644
--- a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdIRI.java
+++ b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdIRI.java
@@ -39,8 +39,12 @@ final class JsonLdIRIImpl extends AbstractJsonLdTermImpl implements JsonLdIRI {
     }
 
     @Override
-    public String ntriplesString() {
-        return "<" + node.getValue() + ">";
+    public boolean equals(final Object obj) {
+        if (!(obj instanceof IRI)) {
+            return false;
+        }
+        final IRI other = (IRI) obj;
+        return node.getValue().equals(other.getIRIString());
     }
 
     @Override
@@ -54,11 +58,7 @@ final class JsonLdIRIImpl extends AbstractJsonLdTermImpl implements JsonLdIRI {
     }
 
     @Override
-    public boolean equals(final Object obj) {
-        if (!(obj instanceof IRI)) {
-            return false;
-        }
-        final IRI other = (IRI) obj;
-        return node.getValue().equals(other.getIRIString());
+    public String ntriplesString() {
+        return "<" + node.getValue() + ">";
     }
 }
diff --git a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdLiteral.java b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdLiteral.java
index 5a5827f1..0f8c43b8 100644
--- a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdLiteral.java
+++ b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdLiteral.java
@@ -32,6 +32,10 @@ public interface JsonLdLiteral extends JsonLdTerm, Literal {
 
 class JsonLdLiteralImpl extends AbstractJsonLdTermImpl implements JsonLdLiteral {
 
+    private static String lowerCase(final String langTag) {
+        return langTag.toLowerCase(Locale.ROOT);
+    }
+
     JsonLdLiteralImpl(final Node node) {
         super(node);
         if (!node.isLiteral()) {
@@ -39,34 +43,18 @@ class JsonLdLiteralImpl extends AbstractJsonLdTermImpl implements JsonLdLiteral
         }
     }
 
-    private static String lowerCase(final String langTag) {
-        return langTag.toLowerCase(Locale.ROOT);
-    }
-
     @Override
-    public String ntriplesString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append('"');
-        // Escape special characters
-        sb.append(getLexicalForm().replace("\\", "\\\\"). // escaped to \\
-                replace("\"", "\\\""). // escaped to \"
-                replace("\r", "\\r"). // escaped to \r
-                replace("\n", "\\n")); // escaped to \n
-        sb.append('"');
-
-        if (getLanguageTag().isPresent()) {
-            sb.append("@");
-            sb.append(getLanguageTag().get());
-        } else if (!getDatatype().equals(Types.XSD_STRING)) {
-            sb.append("^^");
-            sb.append(getDatatype().ntriplesString());
+    public boolean equals(final Object obj) {
+        // COMMONSRDF-56: Do **not** use
+        // asJsonLdNode().compareTo(other.asJsonLdNode())
+        if (obj instanceof Literal) {
+            final Literal other = (Literal) obj;
+            return getLexicalForm().equals(other.getLexicalForm())
+                    && getDatatype().equals(other.getDatatype())
+                    && getLanguageTag().map(JsonLdLiteralImpl::lowerCase)
+                        .equals(other.getLanguageTag().map(JsonLdLiteralImpl::lowerCase));
         }
-        return sb.toString();
-    }
-
-    @Override
-    public String getLexicalForm() {
-        return node.getValue();
+        return false;
     }
 
     @Override
@@ -79,6 +67,11 @@ class JsonLdLiteralImpl extends AbstractJsonLdTermImpl implements JsonLdLiteral
         return Optional.ofNullable(node.getLanguage());
     }
 
+    @Override
+    public String getLexicalForm() {
+        return node.getValue();
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(node.getValue(), node.getDatatype(),
@@ -86,16 +79,23 @@ class JsonLdLiteralImpl extends AbstractJsonLdTermImpl implements JsonLdLiteral
     }
 
     @Override
-    public boolean equals(final Object obj) {
-        // COMMONSRDF-56: Do **not** use
-        // asJsonLdNode().compareTo(other.asJsonLdNode())
-        if (obj instanceof Literal) {
-            final Literal other = (Literal) obj;
-            return getLexicalForm().equals(other.getLexicalForm())
-                    && getDatatype().equals(other.getDatatype())
-                    && getLanguageTag().map(JsonLdLiteralImpl::lowerCase)
-                        .equals(other.getLanguageTag().map(JsonLdLiteralImpl::lowerCase));
+    public String ntriplesString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append('"');
+        // Escape special characters
+        sb.append(getLexicalForm().replace("\\", "\\\\"). // escaped to \\
+                replace("\"", "\\\""). // escaped to \"
+                replace("\r", "\\r"). // escaped to \r
+                replace("\n", "\\n")); // escaped to \n
+        sb.append('"');
+
+        if (getLanguageTag().isPresent()) {
+            sb.append("@");
+            sb.append(getLanguageTag().get());
+        } else if (!getDatatype().equals(Types.XSD_STRING)) {
+            sb.append("^^");
+            sb.append(getDatatype().ntriplesString());
         }
-        return false;
+        return sb.toString();
     }
 }
diff --git a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdQuadLike.java b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdQuadLike.java
index 23678104..1cacb0e5 100644
--- a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdQuadLike.java
+++ b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdQuadLike.java
@@ -43,6 +43,11 @@ class JsonLdQuadLikeImpl<S extends RDFTerm, P extends RDFTerm, O extends RDFTerm
         this.blankNodePrefix = blankNodePrefix;
     }
 
+    @Override
+    public Quad asJsonLdQuad() {
+        return quad;
+    }
+
     @SuppressWarnings("unchecked")
     @Override
     public Optional<G> getGraphName() {
@@ -52,8 +57,8 @@ class JsonLdQuadLikeImpl<S extends RDFTerm, P extends RDFTerm, O extends RDFTerm
 
     @SuppressWarnings("unchecked")
     @Override
-    public S getSubject() {
-        return (S) RDF_TERM_FACTORY.asRDFTerm(quad.getSubject(), blankNodePrefix);
+    public O getObject() {
+        return (O) RDF_TERM_FACTORY.asRDFTerm(quad.getObject(), blankNodePrefix);
     }
 
     @SuppressWarnings("unchecked")
@@ -64,13 +69,8 @@ class JsonLdQuadLikeImpl<S extends RDFTerm, P extends RDFTerm, O extends RDFTerm
 
     @SuppressWarnings("unchecked")
     @Override
-    public O getObject() {
-        return (O) RDF_TERM_FACTORY.asRDFTerm(quad.getObject(), blankNodePrefix);
-    }
-
-    @Override
-    public Quad asJsonLdQuad() {
-        return quad;
+    public S getSubject() {
+        return (S) RDF_TERM_FACTORY.asRDFTerm(quad.getSubject(), blankNodePrefix);
     }
 
     @Override
diff --git a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdRDF.java b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdRDF.java
index 31d975bf..302b3f8f 100644
--- a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdRDF.java
+++ b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdRDF.java
@@ -145,6 +145,29 @@ public final class JsonLdRDF implements RDF {
         return createJsonLdQuad(null, triple.getSubject(), triple.getPredicate(), triple.getObject());
     }
 
+    String asJsonLdString(final BlankNodeOrIRI blankNodeOrIRI) {
+        if (blankNodeOrIRI == null) {
+            return null;
+        }
+        if (blankNodeOrIRI instanceof IRI) {
+            return ((IRI) blankNodeOrIRI).getIRIString();
+        }
+        if (!(blankNodeOrIRI instanceof BlankNode)) {
+            throw new IllegalArgumentException("Expected a BlankNode or IRI, not: " + blankNodeOrIRI);
+        }
+        final BlankNode blankNode = (BlankNode) blankNodeOrIRI;
+        final String ref = blankNode.uniqueReference();
+        if (ref.startsWith(bnodePrefix)) {
+            // One of ours (but possibly not a JsonLdBlankNode) -
+            // we can use the suffix directly
+            return ref.replace(bnodePrefix, "");
+        }
+        // Map to unique bnode identifier, e.g.
+        // _:0dbd92ee-ab1a-45e7-bba2-7ade54f87ec5
+        final UUID uuid = UUID.nameUUIDFromBytes(ref.getBytes(StandardCharsets.UTF_8));
+        return "_:" + uuid;
+    }
+
     /**
      * Adapt a JsonLd {@link com.github.jsonldjava.core.RDFDataset.Quad} as a
      * Commons RDF {@link org.apache.commons.rdf.api.Quad}.
@@ -175,6 +198,26 @@ public final class JsonLdRDF implements RDF {
         return asRDFTerm(node, bnodePrefix);
     }
 
+    JsonLdTerm asRDFTerm(final Node node, final String blankNodePrefix) {
+        if (node == null) {
+            return null; // e.g. default graph
+        }
+        if (node.isIRI()) {
+            return new JsonLdIRIImpl(node);
+        }
+        if (node.isBlankNode()) {
+            return new JsonLdBlankNodeImpl(node, blankNodePrefix);
+        }
+        if (!node.isLiteral()) {
+            throw new IllegalArgumentException("Node is neither IRI, BlankNode nor Literal: " + node);
+        }
+        // TODO: Our own JsonLdLiteral
+        if (node.getLanguage() != null) {
+            return createLiteral(node.getValue(), node.getLanguage());
+        }
+        return createLiteral(node.getValue(), createIRI(node.getDatatype()));
+    }
+
     /**
      * Adapt a JsonLd {@link com.github.jsonldjava.core.RDFDataset.Quad} as a
      * Commons RDF {@link org.apache.commons.rdf.api.Triple}.
@@ -241,6 +284,11 @@ public final class JsonLdRDF implements RDF {
         return new JsonLdIRIImpl(iri);
     }
 
+    RDFDataset.Quad createJsonLdQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        return new RDFDataset.Quad(asJsonLdNode(subject), asJsonLdNode(predicate), asJsonLdNode(object),
+                asJsonLdString(graphName));
+    }
+
     @Override
     public JsonLdLiteral createLiteral(final String literal) {
         return new JsonLdLiteralImpl(new RDFDataset.Literal(literal, null, null));
@@ -267,52 +315,4 @@ public final class JsonLdRDF implements RDF {
         return new JsonLdTripleImpl(createJsonLdQuad(null, subject, predicate, object), bnodePrefix);
     }
 
-    String asJsonLdString(final BlankNodeOrIRI blankNodeOrIRI) {
-        if (blankNodeOrIRI == null) {
-            return null;
-        }
-        if (blankNodeOrIRI instanceof IRI) {
-            return ((IRI) blankNodeOrIRI).getIRIString();
-        }
-        if (!(blankNodeOrIRI instanceof BlankNode)) {
-            throw new IllegalArgumentException("Expected a BlankNode or IRI, not: " + blankNodeOrIRI);
-        }
-        final BlankNode blankNode = (BlankNode) blankNodeOrIRI;
-        final String ref = blankNode.uniqueReference();
-        if (ref.startsWith(bnodePrefix)) {
-            // One of ours (but possibly not a JsonLdBlankNode) -
-            // we can use the suffix directly
-            return ref.replace(bnodePrefix, "");
-        }
-        // Map to unique bnode identifier, e.g.
-        // _:0dbd92ee-ab1a-45e7-bba2-7ade54f87ec5
-        final UUID uuid = UUID.nameUUIDFromBytes(ref.getBytes(StandardCharsets.UTF_8));
-        return "_:" + uuid;
-    }
-
-    JsonLdTerm asRDFTerm(final Node node, final String blankNodePrefix) {
-        if (node == null) {
-            return null; // e.g. default graph
-        }
-        if (node.isIRI()) {
-            return new JsonLdIRIImpl(node);
-        }
-        if (node.isBlankNode()) {
-            return new JsonLdBlankNodeImpl(node, blankNodePrefix);
-        }
-        if (!node.isLiteral()) {
-            throw new IllegalArgumentException("Node is neither IRI, BlankNode nor Literal: " + node);
-        }
-        // TODO: Our own JsonLdLiteral
-        if (node.getLanguage() != null) {
-            return createLiteral(node.getValue(), node.getLanguage());
-        }
-        return createLiteral(node.getValue(), createIRI(node.getDatatype()));
-    }
-
-    RDFDataset.Quad createJsonLdQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        return new RDFDataset.Quad(asJsonLdNode(subject), asJsonLdNode(predicate), asJsonLdNode(object),
-                asJsonLdString(graphName));
-    }
-
 }
\ No newline at end of file
diff --git a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdTerm.java b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdTerm.java
index 8f6a9ee7..27d66158 100644
--- a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdTerm.java
+++ b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdTerm.java
@@ -21,16 +21,6 @@ import org.apache.commons.rdf.api.RDFTerm;
 
 import com.github.jsonldjava.core.RDFDataset.Node;
 
-public interface JsonLdTerm extends RDFTerm {
-
-    /**
-     * Return the underlying JsonLd {@link Node}.
-     *
-     * @return JsonLd {@link Node}
-     */
-    Node asJsonLdNode();
-}
-
 abstract class AbstractJsonLdTermImpl implements JsonLdTerm {
     final Node node;
 
@@ -49,3 +39,13 @@ abstract class AbstractJsonLdTermImpl implements JsonLdTerm {
     }
 
 }
+
+public interface JsonLdTerm extends RDFTerm {
+
+    /**
+     * Return the underlying JsonLd {@link Node}.
+     *
+     * @return JsonLd {@link Node}
+     */
+    Node asJsonLdNode();
+}
diff --git a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdUnionGraph.java b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdUnionGraph.java
index 7a4319af..4634beeb 100644
--- a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdUnionGraph.java
+++ b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/JsonLdUnionGraph.java
@@ -50,10 +50,6 @@ public interface JsonLdUnionGraph extends JsonLdGraphLike<org.apache.commons.rdf
 class JsonLdUnionGraphImpl extends AbstractJsonLdGraphLike<org.apache.commons.rdf.api.Triple>
         implements JsonLdUnionGraph {
 
-    JsonLdUnionGraphImpl(final String bnodePrefix) {
-        super(bnodePrefix);
-    }
-
     JsonLdUnionGraphImpl(final RDFDataset rdfDataSet) {
         super(rdfDataSet);
     }
@@ -62,11 +58,20 @@ class JsonLdUnionGraphImpl extends AbstractJsonLdGraphLike<org.apache.commons.rd
         super(rdfDataSet, bnodePrefix);
     }
 
+    JsonLdUnionGraphImpl(final String bnodePrefix) {
+        super(bnodePrefix);
+    }
+
     @Override
     public void add(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
         super.add(null, subject, predicate, object);
     }
 
+    @Override
+    JsonLdTriple asTripleOrQuad(final com.github.jsonldjava.core.RDFDataset.Quad jsonldQuad) {
+        return factory.asTriple(jsonldQuad);
+    }
+
     @Override
     public boolean contains(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
         return super.contains(null, subject, predicate, object);
@@ -84,12 +89,10 @@ class JsonLdUnionGraphImpl extends AbstractJsonLdGraphLike<org.apache.commons.rd
     }
 
     @Override
-    public Stream<JsonLdTriple> stream(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        return filteredGraphs(null).flatMap(List::stream).filter(quadFilter(subject, predicate, object))
-                .map(factory::asTriple)
-                // Make sure we don't have duplicate triples
-                // NOTE: This can be quite inefficient
-                .distinct();
+    public long size() {
+        // Note: Our specialized stream() already removes duplicates using
+        // .distinct()
+        return stream().count();
     }
 
     @Override
@@ -100,14 +103,11 @@ class JsonLdUnionGraphImpl extends AbstractJsonLdGraphLike<org.apache.commons.rd
     }
 
     @Override
-    JsonLdTriple asTripleOrQuad(final com.github.jsonldjava.core.RDFDataset.Quad jsonldQuad) {
-        return factory.asTriple(jsonldQuad);
-    }
-
-    @Override
-    public long size() {
-        // Note: Our specialized stream() already removes duplicates using
-        // .distinct()
-        return stream().count();
+    public Stream<JsonLdTriple> stream(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        return filteredGraphs(null).flatMap(List::stream).filter(quadFilter(subject, predicate, object))
+                .map(factory::asTriple)
+                // Make sure we don't have duplicate triples
+                // NOTE: This can be quite inefficient
+                .distinct();
     }
 }
diff --git a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/experimental/JsonLdParser.java b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/experimental/JsonLdParser.java
index 430df022..d4242cae 100644
--- a/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/experimental/JsonLdParser.java
+++ b/commons-rdf-jsonld-java/src/main/java/org/apache/commons/rdf/jsonldjava/experimental/JsonLdParser.java
@@ -43,9 +43,19 @@ import com.github.jsonldjava.utils.JsonUtils;
 
 public class JsonLdParser extends AbstractRDFParser<JsonLdParser> {
 
+    private static URL asURL(final IRI iri) throws IllegalStateException {
+        try {
+            return new URI(iri.getIRIString()).toURL();
+        } catch (MalformedURLException | URISyntaxException e) {
+            throw new IllegalStateException("Invalid URL: " + iri.getIRIString());
+        }
+    }
+
     @Override
-    protected JsonLdRDF createRDFTermFactory() {
-        return new JsonLdRDF();
+    protected void checkSource() throws IOException {
+        super.checkSource();
+        // Might throw IllegalStateException if invalid
+        getSourceIri().map(JsonLdParser::asURL);
     }
 
     @Override
@@ -65,19 +75,16 @@ public class JsonLdParser extends AbstractRDFParser<JsonLdParser> {
         return c;
     }
 
-    private static URL asURL(final IRI iri) throws IllegalStateException {
-        try {
-            return new URI(iri.getIRIString()).toURL();
-        } catch (MalformedURLException | URISyntaxException e) {
-            throw new IllegalStateException("Invalid URL: " + iri.getIRIString());
-        }
+    @Override
+    protected JsonLdRDF createRDFTermFactory() {
+        return new JsonLdRDF();
     }
 
-    @Override
-    protected void checkSource() throws IOException {
-        super.checkSource();
-        // Might throw IllegalStateException if invalid
-        getSourceIri().map(JsonLdParser::asURL);
+    private JsonLdRDF getJsonLdFactory() {
+        if (getRdfTermFactory().isPresent() && getRdfTermFactory().get() instanceof JsonLdRDF) {
+            return (JsonLdRDF) getRdfTermFactory().get();
+        }
+        return createRDFTermFactory();
     }
 
     @Override
@@ -131,13 +138,6 @@ public class JsonLdParser extends AbstractRDFParser<JsonLdParser> {
         }
     }
 
-    private JsonLdRDF getJsonLdFactory() {
-        if (getRdfTermFactory().isPresent() && getRdfTermFactory().get() instanceof JsonLdRDF) {
-            return (JsonLdRDF) getRdfTermFactory().get();
-        }
-        return createRDFTermFactory();
-    }
-
     private Object readSource() throws IOException {
         // Due to checked IOException we can't easily
         // do this with .map and .orElseGet()
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4J.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4J.java
index a1ed9e5a..0cc78004 100644
--- a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4J.java
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4J.java
@@ -103,12 +103,6 @@ import org.eclipse.rdf4j.sail.memory.MemoryStore;
 @SuppressWarnings("PMD.UnnecessaryFullyQualifiedName") // we use fully-qualified names for clarity
 public final class RDF4J implements RDF {
 
-    /**
-     * InternalRDF4JFactory is deliberately abstract
-     */
-    private static final InternalRDF4JFactory RDF4J = new InternalRDF4JFactory() {
-    };
-
     public enum Option {
         /**
          * The Graph/Dataset should include any inferred statements
@@ -122,30 +116,58 @@ public final class RDF4J implements RDF {
         handleInitAndShutdown
     }
 
-    private final UUID salt;
-
-    private final ValueFactory valueFactory;
+    /**
+     * InternalRDF4JFactory is deliberately abstract
+     */
+    private static final InternalRDF4JFactory RDF4J = new InternalRDF4JFactory() {
+    };
 
     /**
-     * Construct an {@link RDF4J}.
+     * Adapt a RDF4J {@link Value} as a Commons RDF {@link RDFTerm}.
+     * <p>
+     * The value will be of the same kind as the term, e.g. a
+     * {@link org.eclipse.rdf4j.model.BNode} is converted to a
+     * {@link org.apache.commons.rdf.api.BlankNode}, a
+     * {@link org.eclipse.rdf4j.model.IRI} is converted to a
+     * {@link org.apache.commons.rdf.api.IRI} and a
+     * {@link org.eclipse.rdf4j.model.Literal}. is converted to a
+     * {@link org.apache.commons.rdf.api.Literal}
      *
+     * @param value
+     *            The RDF4J {@link Value} to convert.
+     * @param salt
+     *            A {@link UUID} salt to use for uniquely mapping any
+     *            {@link BNode}s. The salt should typically be the same for
+     *            multiple statements in the same {@link Repository} or
+     *            {@link Model} to ensure {@link BlankNode#equals(Object)} and
+     *            {@link BlankNode#uniqueReference()} works as intended.
+     * @return A {@link RDFTerm} that corresponds to the RDF4J value
+     * @throws IllegalArgumentException
+     *             if the value is not a BNode, Literal or IRI
      */
-    public RDF4J() {
-        this(SimpleValueFactory.getInstance(), UUID.randomUUID());
+    public static RDF4JTerm asRDFTerm(final Value value, final UUID salt) {
+        if (value instanceof BNode) {
+            return RDF4J.createBlankNodeImpl((BNode) value, salt);
+        }
+        if (value instanceof org.eclipse.rdf4j.model.Literal) {
+            return RDF4J.createLiteralImpl((org.eclipse.rdf4j.model.Literal) value);
+        }
+        if (value instanceof org.eclipse.rdf4j.model.IRI) {
+            return RDF4J.createIRIImpl((org.eclipse.rdf4j.model.IRI) value);
+        }
+        throw new IllegalArgumentException("Value is not a BNode, Literal or IRI: " + value.getClass());
     }
 
+    private final UUID salt;
+
+    private final ValueFactory valueFactory;
+
     /**
      * Construct an {@link RDF4J}.
-     * <p>
-     * This constructor is intended for use with the value factory from
-     * {@link Repository#getValueFactory()} when using Repository-based graphs
-     * and datasets.
      *
-     * @param valueFactory
-     *            The RDF4J {@link ValueFactory} to use
      */
-    public RDF4J(final ValueFactory valueFactory) {
-        this(valueFactory, UUID.randomUUID());
+    public RDF4J() {
+        this(SimpleValueFactory.getInstance(), UUID.randomUUID());
     }
 
     /**
@@ -163,6 +185,20 @@ public final class RDF4J implements RDF {
         this(SimpleValueFactory.getInstance(), salt);
     }
 
+    /**
+     * Construct an {@link RDF4J}.
+     * <p>
+     * This constructor is intended for use with the value factory from
+     * {@link Repository#getValueFactory()} when using Repository-based graphs
+     * and datasets.
+     *
+     * @param valueFactory
+     *            The RDF4J {@link ValueFactory} to use
+     */
+    public RDF4J(final ValueFactory valueFactory) {
+        this(valueFactory, UUID.randomUUID());
+    }
+
     /**
      * Construct an {@link RDF4J}.
      * <p>
@@ -182,56 +218,137 @@ public final class RDF4J implements RDF {
     }
 
     /**
-     * Adapt a RDF4J {@link Statement} as a Commons RDF {@link Quad}.
+     * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Dataset}.
      * <p>
-     * For the purpose of {@link BlankNode} equivalence, this method will use an
-     * internal salt UUID that is unique per instance of {@link RDF4J}.
+     * Changes to the dataset are reflected in the repository, and vice versa.
      * <p>
-     * <strong>NOTE:</strong> If combining RDF4J {@link Statement}s multiple
-     * repositories or models, then their {@link BNode}s may have the same
-     * {@link BNode#getID()}, which with this method would become equivalent
-     * according to {@link BlankNode#equals(Object)} and
-     * {@link BlankNode#uniqueReference()}, unless a separate {@link RDF4J}
-     * instance is used per RDF4J repository/model.
+     * <strong>Note:</strong> Some operations on the {@link RDF4JDataset}
+     * requires the use of try-with-resources to close underlying
+     * {@link RepositoryConnection}s, including {@link RDF4JDataset#iterate()},
+     * {@link RDF4JDataset#stream()} and {@link RDF4JDataset#getGraphNames()}.
      *
-     * @param statement
-     *            The statement to convert
-     * @return A {@link RDF4JQuad} that is equivalent to the statement
+     * @param repository
+     *            RDF4J {@link Repository} to connect to.
+     * @param options
+     *            Zero or more {@link Option}
+     * @return A {@link Dataset} backed by the RDF4J repository.
      */
-    public RDF4JQuad asQuad(final Statement statement) {
-        return RDF4J.createQuadImpl(statement, salt);
+    public RDF4JDataset asDataset(final Repository repository, final Option... options) {
+        final EnumSet<Option> opts = optionSet(options);
+        return RDF4J.createRepositoryDatasetImpl(repository, opts.contains(Option.handleInitAndShutdown),
+                opts.contains(Option.includeInferred));
     }
 
     /**
+     * Adapt an RDF4J {@link Model} as a Commons RDF {@link Graph}.
+     * <p>
+     * Changes to the graph are reflected in the model, and vice versa.
      *
-     * Adapt a RDF4J {@link Value} as a Commons RDF {@link RDFTerm}.
+     * @param model
+     *            RDF4J {@link Model} to adapt.
+     * @return Adapted {@link Graph}.
+     */
+    public RDF4JGraph asGraph(final Model model) {
+        return RDF4J.createModelGraphImpl(model, this);
+    }
+
+    /**
+     * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Graph}.
      * <p>
-     * The value will be of the same kind as the term, e.g. a
-     * {@link org.eclipse.rdf4j.model.BNode} is converted to a
-     * {@link org.apache.commons.rdf.api.BlankNode}, a
-     * {@link org.eclipse.rdf4j.model.IRI} is converted to a
-     * {@link org.apache.commons.rdf.api.IRI} and a
-     * {@link org.eclipse.rdf4j.model.Literal}. is converted to a
-     * {@link org.apache.commons.rdf.api.Literal}
+     * The graph will only include triples in the default graph (equivalent to
+     * context <code>new Resource[0]{null})</code> in RDF4J).
+     * <p>
+     * Changes to the graph are reflected in the repository, and vice versa.
+     * <p>
+     * <strong>Note:</strong> Some operations on the {@link RDF4JGraph} requires
+     * the use of try-with-resources to close underlying
+     * {@link RepositoryConnection}s, including {@link RDF4JGraph#iterate()} and
+     * {@link RDF4JGraph#stream()}.
+     *
+     * @param repository
+     *            RDF4J {@link Repository} to connect to.
+     * @param options
+     *            Zero or more {@link Option}
+     * @return A {@link Graph} backed by the RDF4J repository.
+     */
+    public RDF4JGraph asGraph(final Repository repository, final Option... options) {
+        final EnumSet<Option> opts = optionSet(options);
+        return RDF4J.createRepositoryGraphImpl(repository, opts.contains(Option.handleInitAndShutdown),
+                opts.contains(Option.includeInferred), new Resource[] { null }); // default
+                                                                                 // graph
+    }
+
+    /**
+     * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Graph}.
+     * <p>
+     * The graph will include triples in the specified contexts.
+     * <p>
+     * Changes to the graph are reflected in the repository, and vice versa.
+     * Triples added/removed to the graph are reflected in all the specified
+     * contexts.
+     * <p>
+     * <strong>Note:</strong> Some operations on the {@link RDF4JGraph} requires
+     * the use of try-with-resources to close underlying
+     * {@link RepositoryConnection}s, including {@link RDF4JGraph#iterate()} and
+     * {@link RDF4JGraph#stream()}.
+     *
+     * @param repository
+     *            RDF4J {@link Repository} to connect to.
+     * @param contexts
+     *            A {@link Set} of {@link BlankNodeOrIRI} specifying the graph
+     *            names to use as a context. The set may include the value
+     *            <code>null</code> to indicate the default graph. The empty set
+     *            indicates any context, e.g. the <em>union graph</em>.
+     * @param option
+     *            Zero or more {@link Option}s
+     * @return A {@link Graph} backed by the RDF4J repository.
+     */
+    public RDF4JGraph asGraph(final Repository repository, final Set<? extends BlankNodeOrIRI> contexts, final Option... option) {
+        final EnumSet<Option> opts = optionSet(option);
+        /** NOTE: asValue() deliberately CAN handle <code>null</code> */
+        final Resource[] resources = contexts.stream().map(g -> (Resource) asValue(g)).toArray(Resource[]::new);
+        return RDF4J.createRepositoryGraphImpl(Objects.requireNonNull(repository),
+                opts.contains(Option.handleInitAndShutdown), opts.contains(Option.includeInferred), resources);
+    }
+
+    /**
+     * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Graph}.
+     * <p>
+     * The graph will include triples in any contexts (e.g. the union graph).
+     * <p>
+     * Changes to the graph are reflected in the repository, and vice versa.
+     *
+     * @param repository
+     *            RDF4J {@link Repository} to connect to.
+     * @param options
+     *            Zero or more {@link Option}
+     * @return A union {@link Graph} backed by the RDF4J repository.
+     */
+    public RDF4JGraph asGraphUnion(final Repository repository, final Option... options) {
+        final EnumSet<Option> opts = optionSet(options);
+        return RDF4J.createRepositoryGraphImpl(repository, opts.contains(Option.handleInitAndShutdown),
+                opts.contains(Option.includeInferred)); // union graph
+    }
+
+    /**
+     * Adapt a RDF4J {@link Statement} as a Commons RDF {@link Quad}.
      * <p>
      * For the purpose of {@link BlankNode} equivalence, this method will use an
      * internal salt UUID that is unique per instance of {@link RDF4J}.
      * <p>
-     * <strong>NOTE:</strong> If combining RDF4J values from multiple
+     * <strong>NOTE:</strong> If combining RDF4J {@link Statement}s multiple
      * repositories or models, then their {@link BNode}s may have the same
      * {@link BNode#getID()}, which with this method would become equivalent
      * according to {@link BlankNode#equals(Object)} and
      * {@link BlankNode#uniqueReference()}, unless a separate {@link RDF4J}
      * instance is used per RDF4J repository/model.
      *
-     * @param value
-     *            The RDF4J {@link Value} to convert.
-     * @return A {@link RDFTerm} that corresponds to the RDF4J value
-     * @throws IllegalArgumentException
-     *             if the value is not a BNode, Literal or IRI
+     * @param statement
+     *            The statement to convert
+     * @return A {@link RDF4JQuad} that is equivalent to the statement
      */
-    public RDF4JTerm asRDFTerm(final Value value) {
-        return asRDFTerm(value, salt);
+    public RDF4JQuad asQuad(final Statement statement) {
+        return RDF4J.createQuadImpl(statement, salt);
     }
 
     /**
@@ -307,6 +424,7 @@ public final class RDF4J implements RDF {
     }
 
     /**
+     *
      * Adapt a RDF4J {@link Value} as a Commons RDF {@link RDFTerm}.
      * <p>
      * The value will be of the same kind as the term, e.g. a
@@ -316,143 +434,25 @@ public final class RDF4J implements RDF {
      * {@link org.apache.commons.rdf.api.IRI} and a
      * {@link org.eclipse.rdf4j.model.Literal}. is converted to a
      * {@link org.apache.commons.rdf.api.Literal}
+     * <p>
+     * For the purpose of {@link BlankNode} equivalence, this method will use an
+     * internal salt UUID that is unique per instance of {@link RDF4J}.
+     * <p>
+     * <strong>NOTE:</strong> If combining RDF4J values from multiple
+     * repositories or models, then their {@link BNode}s may have the same
+     * {@link BNode#getID()}, which with this method would become equivalent
+     * according to {@link BlankNode#equals(Object)} and
+     * {@link BlankNode#uniqueReference()}, unless a separate {@link RDF4J}
+     * instance is used per RDF4J repository/model.
      *
      * @param value
      *            The RDF4J {@link Value} to convert.
-     * @param salt
-     *            A {@link UUID} salt to use for uniquely mapping any
-     *            {@link BNode}s. The salt should typically be the same for
-     *            multiple statements in the same {@link Repository} or
-     *            {@link Model} to ensure {@link BlankNode#equals(Object)} and
-     *            {@link BlankNode#uniqueReference()} works as intended.
      * @return A {@link RDFTerm} that corresponds to the RDF4J value
      * @throws IllegalArgumentException
      *             if the value is not a BNode, Literal or IRI
      */
-    public static RDF4JTerm asRDFTerm(final Value value, final UUID salt) {
-        if (value instanceof BNode) {
-            return RDF4J.createBlankNodeImpl((BNode) value, salt);
-        }
-        if (value instanceof org.eclipse.rdf4j.model.Literal) {
-            return RDF4J.createLiteralImpl((org.eclipse.rdf4j.model.Literal) value);
-        }
-        if (value instanceof org.eclipse.rdf4j.model.IRI) {
-            return RDF4J.createIRIImpl((org.eclipse.rdf4j.model.IRI) value);
-        }
-        throw new IllegalArgumentException("Value is not a BNode, Literal or IRI: " + value.getClass());
-    }
-
-    /**
-     * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Dataset}.
-     * <p>
-     * Changes to the dataset are reflected in the repository, and vice versa.
-     * <p>
-     * <strong>Note:</strong> Some operations on the {@link RDF4JDataset}
-     * requires the use of try-with-resources to close underlying
-     * {@link RepositoryConnection}s, including {@link RDF4JDataset#iterate()},
-     * {@link RDF4JDataset#stream()} and {@link RDF4JDataset#getGraphNames()}.
-     *
-     * @param repository
-     *            RDF4J {@link Repository} to connect to.
-     * @param options
-     *            Zero or more {@link Option}
-     * @return A {@link Dataset} backed by the RDF4J repository.
-     */
-    public RDF4JDataset asDataset(final Repository repository, final Option... options) {
-        final EnumSet<Option> opts = optionSet(options);
-        return RDF4J.createRepositoryDatasetImpl(repository, opts.contains(Option.handleInitAndShutdown),
-                opts.contains(Option.includeInferred));
-    }
-
-    /**
-     * Adapt an RDF4J {@link Model} as a Commons RDF {@link Graph}.
-     * <p>
-     * Changes to the graph are reflected in the model, and vice versa.
-     *
-     * @param model
-     *            RDF4J {@link Model} to adapt.
-     * @return Adapted {@link Graph}.
-     */
-    public RDF4JGraph asGraph(final Model model) {
-        return RDF4J.createModelGraphImpl(model, this);
-    }
-
-    /**
-     * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Graph}.
-     * <p>
-     * The graph will only include triples in the default graph (equivalent to
-     * context <code>new Resource[0]{null})</code> in RDF4J).
-     * <p>
-     * Changes to the graph are reflected in the repository, and vice versa.
-     * <p>
-     * <strong>Note:</strong> Some operations on the {@link RDF4JGraph} requires
-     * the use of try-with-resources to close underlying
-     * {@link RepositoryConnection}s, including {@link RDF4JGraph#iterate()} and
-     * {@link RDF4JGraph#stream()}.
-     *
-     * @param repository
-     *            RDF4J {@link Repository} to connect to.
-     * @param options
-     *            Zero or more {@link Option}
-     * @return A {@link Graph} backed by the RDF4J repository.
-     */
-    public RDF4JGraph asGraph(final Repository repository, final Option... options) {
-        final EnumSet<Option> opts = optionSet(options);
-        return RDF4J.createRepositoryGraphImpl(repository, opts.contains(Option.handleInitAndShutdown),
-                opts.contains(Option.includeInferred), new Resource[] { null }); // default
-                                                                                 // graph
-    }
-
-    /**
-     * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Graph}.
-     * <p>
-     * The graph will include triples in any contexts (e.g. the union graph).
-     * <p>
-     * Changes to the graph are reflected in the repository, and vice versa.
-     *
-     * @param repository
-     *            RDF4J {@link Repository} to connect to.
-     * @param options
-     *            Zero or more {@link Option}
-     * @return A union {@link Graph} backed by the RDF4J repository.
-     */
-    public RDF4JGraph asGraphUnion(final Repository repository, final Option... options) {
-        final EnumSet<Option> opts = optionSet(options);
-        return RDF4J.createRepositoryGraphImpl(repository, opts.contains(Option.handleInitAndShutdown),
-                opts.contains(Option.includeInferred)); // union graph
-    }
-
-    /**
-     * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Graph}.
-     * <p>
-     * The graph will include triples in the specified contexts.
-     * <p>
-     * Changes to the graph are reflected in the repository, and vice versa.
-     * Triples added/removed to the graph are reflected in all the specified
-     * contexts.
-     * <p>
-     * <strong>Note:</strong> Some operations on the {@link RDF4JGraph} requires
-     * the use of try-with-resources to close underlying
-     * {@link RepositoryConnection}s, including {@link RDF4JGraph#iterate()} and
-     * {@link RDF4JGraph#stream()}.
-     *
-     * @param repository
-     *            RDF4J {@link Repository} to connect to.
-     * @param contexts
-     *            A {@link Set} of {@link BlankNodeOrIRI} specifying the graph
-     *            names to use as a context. The set may include the value
-     *            <code>null</code> to indicate the default graph. The empty set
-     *            indicates any context, e.g. the <em>union graph</em>.
-     * @param option
-     *            Zero or more {@link Option}s
-     * @return A {@link Graph} backed by the RDF4J repository.
-     */
-    public RDF4JGraph asGraph(final Repository repository, final Set<? extends BlankNodeOrIRI> contexts, final Option... option) {
-        final EnumSet<Option> opts = optionSet(option);
-        /** NOTE: asValue() deliberately CAN handle <code>null</code> */
-        final Resource[] resources = contexts.stream().map(g -> (Resource) asValue(g)).toArray(Resource[]::new);
-        return RDF4J.createRepositoryGraphImpl(Objects.requireNonNull(repository),
-                opts.contains(Option.handleInitAndShutdown), opts.contains(Option.includeInferred), resources);
+    public RDF4JTerm asRDFTerm(final Value value) {
+        return asRDFTerm(value, salt);
     }
 
     /**
@@ -628,21 +628,21 @@ public final class RDF4J implements RDF {
     }
 
     @Override
-    public RDF4JTriple createTriple(final BlankNodeOrIRI subject, final org.apache.commons.rdf.api.IRI predicate, final RDFTerm object)
-            throws IllegalArgumentException {
+    public Quad createQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final org.apache.commons.rdf.api.IRI predicate,
+            final RDFTerm object) throws IllegalArgumentException {
         final Statement statement = getValueFactory().createStatement(
                 (org.eclipse.rdf4j.model.Resource) asValue(subject), (org.eclipse.rdf4j.model.IRI) asValue(predicate),
-                asValue(object));
-        return asTriple(statement);
+                asValue(object), (org.eclipse.rdf4j.model.Resource) asValue(graphName));
+        return asQuad(statement);
     }
 
     @Override
-    public Quad createQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final org.apache.commons.rdf.api.IRI predicate,
-            final RDFTerm object) throws IllegalArgumentException {
+    public RDF4JTriple createTriple(final BlankNodeOrIRI subject, final org.apache.commons.rdf.api.IRI predicate, final RDFTerm object)
+            throws IllegalArgumentException {
         final Statement statement = getValueFactory().createStatement(
                 (org.eclipse.rdf4j.model.Resource) asValue(subject), (org.eclipse.rdf4j.model.IRI) asValue(predicate),
-                asValue(object), (org.eclipse.rdf4j.model.Resource) asValue(graphName));
-        return asQuad(statement);
+                asValue(object));
+        return asTriple(statement);
     }
 
     public ValueFactory getValueFactory() {
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JDataset.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JDataset.java
index 8085160a..cd0d95da 100644
--- a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JDataset.java
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JDataset.java
@@ -44,98 +44,98 @@ public interface RDF4JDataset extends Dataset, RDF4JGraphLike<Quad> {
      * This can generally achieved using a try-with-resources block, e.g.:
      *
      * <pre>
-     * int subjects;
-     * try (Stream&lt;RDF4JQuad&gt; s : graph.stream()) {
-     *   subjects = s.map(RDF4JQuad::getSubject).distinct().count()
+     * int graphs;
+     * try (Stream&lt;BlankNodeOrIRI&gt; s : graph.stream()) {
+     *   graphs = s.count()
      * }
      * </pre>
      */
     @Override
-    Stream<RDF4JQuad> stream();
+    Stream<BlankNodeOrIRI> getGraphNames();
 
     /**
      * {@inheritDoc}
      * <p>
      * Note that for datasets backed by a repository ({@link #asRepository()} is
-     * present), the stream <strong>must be closed</strong> with
-     * {@link Stream#close()}.
+     * present), the iterable <strong>must be closed</strong> with
+     * {@link ClosableIterable#close()}.
      * <p>
      * This can generally achieved using a try-with-resources block, e.g.:
      *
      * <pre>
-     * int subjects;
-     * try (Stream&lt;RDF4JQuad&gt; s : graph.stream()) {
-     *   subjects = s.map(RDF4JQuad::getSubject).distinct().count()
+     * try (ClosableIterable&lt;Quad&gt; s : graph.iterate()) {
+     *   for (Quad q : quads) {
+     *       return q; // OK to terminate for-loop early
+     *   }
      * }
      * </pre>
+     *
+     * If you don't use a try-with-resources block, the iterator will attempt to
+     * close the ClosableIterable when reaching the end of the iteration.
      */
     @Override
-    Stream<RDF4JQuad> stream(Optional<BlankNodeOrIRI> graphName, BlankNodeOrIRI subject, IRI predicate, RDFTerm object);
+    ClosableIterable<Quad> iterate();
 
     /**
      * {@inheritDoc}
      * <p>
      * Note that for datasets backed by a repository ({@link #asRepository()} is
-     * present), the stream <strong>must be closed</strong> with
-     * {@link Stream#close()}.
+     * present), the iterable <strong>must be closed</strong> with
+     * {@link ClosableIterable#close()}.
      * <p>
      * This can generally achieved using a try-with-resources block, e.g.:
      *
      * <pre>
-     * int graphs;
-     * try (Stream&lt;BlankNodeOrIRI&gt; s : graph.stream()) {
-     *   graphs = s.count()
+     * try (ClosableIterable&lt;Quad&gt; s : graph.iterate(g,s,p,o)) {
+     *   for (Quad q : quads) {
+     *       return q; // OK to terminate for-loop early
+     *   }
      * }
      * </pre>
+     *
+     * If you don't use a try-with-resources block, the iterator will attempt to
+     * close the ClosableIterable when reaching the end of the iteration.
      */
     @Override
-    Stream<BlankNodeOrIRI> getGraphNames();
+    ClosableIterable<Quad> iterate(Optional<BlankNodeOrIRI> graphName, BlankNodeOrIRI subject, IRI predicate,
+            RDFTerm object);
 
     /**
      * {@inheritDoc}
      * <p>
      * Note that for datasets backed by a repository ({@link #asRepository()} is
-     * present), the iterable <strong>must be closed</strong> with
-     * {@link ClosableIterable#close()}.
+     * present), the stream <strong>must be closed</strong> with
+     * {@link Stream#close()}.
      * <p>
      * This can generally achieved using a try-with-resources block, e.g.:
      *
      * <pre>
-     * try (ClosableIterable&lt;Quad&gt; s : graph.iterate()) {
-     *   for (Quad q : quads) {
-     *       return q; // OK to terminate for-loop early
-     *   }
+     * int subjects;
+     * try (Stream&lt;RDF4JQuad&gt; s : graph.stream()) {
+     *   subjects = s.map(RDF4JQuad::getSubject).distinct().count()
      * }
      * </pre>
-     *
-     * If you don't use a try-with-resources block, the iterator will attempt to
-     * close the ClosableIterable when reaching the end of the iteration.
      */
     @Override
-    ClosableIterable<Quad> iterate();
+    Stream<RDF4JQuad> stream();
 
     /**
      * {@inheritDoc}
      * <p>
      * Note that for datasets backed by a repository ({@link #asRepository()} is
-     * present), the iterable <strong>must be closed</strong> with
-     * {@link ClosableIterable#close()}.
+     * present), the stream <strong>must be closed</strong> with
+     * {@link Stream#close()}.
      * <p>
      * This can generally achieved using a try-with-resources block, e.g.:
      *
      * <pre>
-     * try (ClosableIterable&lt;Quad&gt; s : graph.iterate(g,s,p,o)) {
-     *   for (Quad q : quads) {
-     *       return q; // OK to terminate for-loop early
-     *   }
+     * int subjects;
+     * try (Stream&lt;RDF4JQuad&gt; s : graph.stream()) {
+     *   subjects = s.map(RDF4JQuad::getSubject).distinct().count()
      * }
      * </pre>
-     *
-     * If you don't use a try-with-resources block, the iterator will attempt to
-     * close the ClosableIterable when reaching the end of the iteration.
      */
     @Override
-    ClosableIterable<Quad> iterate(Optional<BlankNodeOrIRI> graphName, BlankNodeOrIRI subject, IRI predicate,
-            RDFTerm object);
+    Stream<RDF4JQuad> stream(Optional<BlankNodeOrIRI> graphName, BlankNodeOrIRI subject, IRI predicate, RDFTerm object);
 
 }
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JGraph.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JGraph.java
index 465af5d5..f25cf2c6 100644
--- a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JGraph.java
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JGraph.java
@@ -74,83 +74,83 @@ public interface RDF4JGraph extends Graph, RDF4JGraphLike<Triple> {
      * {@inheritDoc}
      * <p>
      * Note that for graphs backed by a repository ({@link #asRepository()} is
-     * present), the stream <strong>must be closed</strong> with
-     * {@link Stream#close()}.
+     * present), the iterable <strong>must be closed</strong> with
+     * {@link ClosableIterable#close()}.
      * <p>
      * This can generally achieved using a try-with-resources block, e.g.:
      *
      * <pre>
-     * int subjects;
-     * try (Stream&lt;RDF4JTriple&gt; s : graph.stream()) {
-     *   subjects = s.map(RDF4JTriple::getSubject).distinct().count()
+     * try (ClosableIterable&lt;Triple&gt; s : graph.iterate()) {
+     *   for (Triple t : triples) {
+     *       return t; // OK to terminate for-loop early
+     *   }
      * }
      * </pre>
+     *
+     * If you don't use a try-with-resources block, the iterator will attempt to
+     * close the ClosableIterable when reaching the end of the iteration.
      */
     @Override
-    Stream<RDF4JTriple> stream();
+    ClosableIterable<Triple> iterate() throws ConcurrentModificationException, IllegalStateException;
 
     /**
      * {@inheritDoc}
      * <p>
      * Note that for graphs backed by a repository ({@link #asRepository()} is
-     * present), the stream <strong>must be closed</strong> with
-     * {@link Stream#close()}.
+     * present), the iterable <strong>must be closed</strong> with
+     * {@link ClosableIterable#close()}.
      * <p>
      * This can generally achieved using a try-with-resources block, e.g.:
      *
      * <pre>
-     * int subjects;
-     * try (Stream&lt;RDF4JTriple&gt; s : graph.stream(s,p,o)) {
-     *   subjects = s.map(RDF4JTriple::getSubject).distinct().count()
+     * try (ClosableIterable&lt;Triple&gt; s : graph.iterate(s,p,o)) {
+     *   for (Triple t : triples) {
+     *       return t; // OK to terminate for-loop early
+     *   }
      * }
      * </pre>
+     *
+     * If you don't use a try-with-resources block, the iterator will attempt to
+     * close the ClosableIterable when reaching the end of the iteration.
      */
     @Override
-    Stream<RDF4JTriple> stream(BlankNodeOrIRI subject, IRI predicate, RDFTerm object);
+    ClosableIterable<Triple> iterate(BlankNodeOrIRI subject, IRI predicate, RDFTerm object);
 
     /**
      * {@inheritDoc}
      * <p>
      * Note that for graphs backed by a repository ({@link #asRepository()} is
-     * present), the iterable <strong>must be closed</strong> with
-     * {@link ClosableIterable#close()}.
+     * present), the stream <strong>must be closed</strong> with
+     * {@link Stream#close()}.
      * <p>
      * This can generally achieved using a try-with-resources block, e.g.:
      *
      * <pre>
-     * try (ClosableIterable&lt;Triple&gt; s : graph.iterate()) {
-     *   for (Triple t : triples) {
-     *       return t; // OK to terminate for-loop early
-     *   }
+     * int subjects;
+     * try (Stream&lt;RDF4JTriple&gt; s : graph.stream()) {
+     *   subjects = s.map(RDF4JTriple::getSubject).distinct().count()
      * }
      * </pre>
-     *
-     * If you don't use a try-with-resources block, the iterator will attempt to
-     * close the ClosableIterable when reaching the end of the iteration.
      */
     @Override
-    ClosableIterable<Triple> iterate() throws ConcurrentModificationException, IllegalStateException;
+    Stream<RDF4JTriple> stream();
 
     /**
      * {@inheritDoc}
      * <p>
      * Note that for graphs backed by a repository ({@link #asRepository()} is
-     * present), the iterable <strong>must be closed</strong> with
-     * {@link ClosableIterable#close()}.
+     * present), the stream <strong>must be closed</strong> with
+     * {@link Stream#close()}.
      * <p>
      * This can generally achieved using a try-with-resources block, e.g.:
      *
      * <pre>
-     * try (ClosableIterable&lt;Triple&gt; s : graph.iterate(s,p,o)) {
-     *   for (Triple t : triples) {
-     *       return t; // OK to terminate for-loop early
-     *   }
+     * int subjects;
+     * try (Stream&lt;RDF4JTriple&gt; s : graph.stream(s,p,o)) {
+     *   subjects = s.map(RDF4JTriple::getSubject).distinct().count()
      * }
      * </pre>
-     *
-     * If you don't use a try-with-resources block, the iterator will attempt to
-     * close the ClosableIterable when reaching the end of the iteration.
      */
     @Override
-    ClosableIterable<Triple> iterate(BlankNodeOrIRI subject, IRI predicate, RDFTerm object);
+    Stream<RDF4JTriple> stream(BlankNodeOrIRI subject, IRI predicate, RDFTerm object);
 }
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/experimental/RDF4JParser.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/experimental/RDF4JParser.java
index ad38c3a9..da0fe2c9 100644
--- a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/experimental/RDF4JParser.java
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/experimental/RDF4JParser.java
@@ -57,6 +57,24 @@ import org.eclipse.rdf4j.rio.helpers.AbstractRDFHandler;
  */
 public class RDF4JParser extends AbstractRDFParser<RDF4JParser> {
 
+    private final static class AddToModel extends AbstractRDFHandler {
+        private final Model model;
+
+        public AddToModel(final Model model) {
+            this.model = model;
+        }
+
+        @Override
+        public void handleNamespace(final String prefix, final String uri) throws RDFHandlerException {
+            model.setNamespace(prefix, uri);
+        }
+
+        @Override
+        public void handleStatement(final org.eclipse.rdf4j.model.Statement st) throws RDFHandlerException {
+            model.add(st);
+        }
+    }
+
     private final class AddToQuadConsumer extends AbstractRDFHandler {
         private final Consumer<Quad> quadTarget;
 
@@ -81,24 +99,6 @@ public class RDF4JParser extends AbstractRDFParser<RDF4JParser> {
         }
     }
 
-    private final static class AddToModel extends AbstractRDFHandler {
-        private final Model model;
-
-        public AddToModel(final Model model) {
-            this.model = model;
-        }
-
-        @Override
-        public void handleStatement(final org.eclipse.rdf4j.model.Statement st) throws RDFHandlerException {
-            model.add(st);
-        }
-
-        @Override
-        public void handleNamespace(final String prefix, final String uri) throws RDFHandlerException {
-            model.setNamespace(prefix, uri);
-        }
-    }
-
     private RDF4J rdf4jTermFactory;
     private ParserConfig parserConfig = new ParserConfig();
 
@@ -107,60 +107,6 @@ public class RDF4JParser extends AbstractRDFParser<RDF4JParser> {
         return new RDF4J();
     }
 
-    @Override
-    protected RDF4JParser prepareForParsing() throws IOException, IllegalStateException {
-        final RDF4JParser c = super.prepareForParsing();
-        // Ensure we have an RDF4J for conversion.
-        // We'll make a new one if user has provided a non-RDF4J factory
-        c.rdf4jTermFactory = (RDF4J) getRdfTermFactory().filter(RDF4J.class::isInstance)
-                .orElseGet(c::createRDFTermFactory);
-        return c;
-    }
-
-    @Override
-    protected void parseSynchronusly() throws IOException {
-        final Optional<RDFFormat> formatByMimeType = getContentType().flatMap(Rio::getParserFormatForMIMEType);
-        final String base = getBase().map(IRI::getIRIString).orElse(null);
-
-        final ParserConfig parserConfig = getParserConfig();
-        // TODO: Should we need to set anything?
-        final RDFLoader loader = new RDFLoader(parserConfig, rdf4jTermFactory.getValueFactory());
-        final RDFHandler rdfHandler = makeRDFHandler();
-        if (getSourceFile().isPresent()) {
-            // NOTE: While we could have used
-            // loader.load(sourcePath.toFile()
-            // if the path fs provider == FileSystems.getDefault(),
-            // that RDFLoader method does not use absolute path
-            // as the base URI, so to be consistent
-            // we'll always do it with our own input stream
-            //
-            // That means we may have to guess format by extensions:
-            final Optional<RDFFormat> formatByFileName = getSourceFile().map(Path::getFileName).map(Path::toString)
-                    .flatMap(Rio::getParserFormatForFileName);
-            // TODO: for the excited.. what about the extension after following
-            // symlinks?
-
-            final RDFFormat format = formatByMimeType.orElse(formatByFileName.orElse(null));
-            try (InputStream in = Files.newInputStream(getSourceFile().get())) {
-                loader.load(in, base, format, rdfHandler);
-            }
-        } else if (getSourceIri().isPresent()) {
-            try {
-                // TODO: Handle international IRIs properly
-                // (Unicode support for hostname, path and query)
-                final URL url = new URL(getSourceIri().get().getIRIString());
-                // TODO: This probably does not support https:// -> http://
-                // redirections
-                loader.load(url, base, formatByMimeType.orElse(null), makeRDFHandler());
-            } catch (final MalformedURLException ex) {
-                throw new IOException("Can't handle source URL: " + getSourceIri().get(), ex);
-            }
-        }
-        // must be getSourceInputStream then, this is guaranteed by
-        // super.checkSource();
-        loader.load(getSourceInputStream().get(), base, formatByMimeType.orElse(null), rdfHandler);
-    }
-
     /**
      * Gets the RDF4J {@link ParserConfig} to use.
      * <p>
@@ -177,16 +123,6 @@ public class RDF4JParser extends AbstractRDFParser<RDF4JParser> {
         return parserConfig;
     }
 
-    /**
-     * Sets an RDF4J {@link ParserConfig} to use
-     *
-     * @param parserConfig
-     *            Parser configuration
-     */
-    public void setParserConfig(final ParserConfig parserConfig) {
-        this.parserConfig = parserConfig;
-    }
-
     protected RDFHandler makeRDFHandler() {
 
         // TODO: Can we join the below DF4JDataset and RDF4JGraph cases
@@ -231,4 +167,68 @@ public class RDF4JParser extends AbstractRDFParser<RDF4JParser> {
         return new AddToQuadConsumer(getTarget());
     }
 
+    @Override
+    protected void parseSynchronusly() throws IOException {
+        final Optional<RDFFormat> formatByMimeType = getContentType().flatMap(Rio::getParserFormatForMIMEType);
+        final String base = getBase().map(IRI::getIRIString).orElse(null);
+
+        final ParserConfig parserConfig = getParserConfig();
+        // TODO: Should we need to set anything?
+        final RDFLoader loader = new RDFLoader(parserConfig, rdf4jTermFactory.getValueFactory());
+        final RDFHandler rdfHandler = makeRDFHandler();
+        if (getSourceFile().isPresent()) {
+            // NOTE: While we could have used
+            // loader.load(sourcePath.toFile()
+            // if the path fs provider == FileSystems.getDefault(),
+            // that RDFLoader method does not use absolute path
+            // as the base URI, so to be consistent
+            // we'll always do it with our own input stream
+            //
+            // That means we may have to guess format by extensions:
+            final Optional<RDFFormat> formatByFileName = getSourceFile().map(Path::getFileName).map(Path::toString)
+                    .flatMap(Rio::getParserFormatForFileName);
+            // TODO: for the excited.. what about the extension after following
+            // symlinks?
+
+            final RDFFormat format = formatByMimeType.orElse(formatByFileName.orElse(null));
+            try (InputStream in = Files.newInputStream(getSourceFile().get())) {
+                loader.load(in, base, format, rdfHandler);
+            }
+        } else if (getSourceIri().isPresent()) {
+            try {
+                // TODO: Handle international IRIs properly
+                // (Unicode support for hostname, path and query)
+                final URL url = new URL(getSourceIri().get().getIRIString());
+                // TODO: This probably does not support https:// -> http://
+                // redirections
+                loader.load(url, base, formatByMimeType.orElse(null), makeRDFHandler());
+            } catch (final MalformedURLException ex) {
+                throw new IOException("Can't handle source URL: " + getSourceIri().get(), ex);
+            }
+        }
+        // must be getSourceInputStream then, this is guaranteed by
+        // super.checkSource();
+        loader.load(getSourceInputStream().get(), base, formatByMimeType.orElse(null), rdfHandler);
+    }
+
+    @Override
+    protected RDF4JParser prepareForParsing() throws IOException, IllegalStateException {
+        final RDF4JParser c = super.prepareForParsing();
+        // Ensure we have an RDF4J for conversion.
+        // We'll make a new one if user has provided a non-RDF4J factory
+        c.rdf4jTermFactory = (RDF4J) getRdfTermFactory().filter(RDF4J.class::isInstance)
+                .orElseGet(c::createRDFTermFactory);
+        return c;
+    }
+
+    /**
+     * Sets an RDF4J {@link ParserConfig} to use
+     *
+     * @param parserConfig
+     *            Parser configuration
+     */
+    public void setParserConfig(final ParserConfig parserConfig) {
+        this.parserConfig = parserConfig;
+    }
+
 }
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/AbstractRepositoryGraphLike.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/AbstractRepositoryGraphLike.java
index 2276c59f..c1db01b8 100644
--- a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/AbstractRepositoryGraphLike.java
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/AbstractRepositoryGraphLike.java
@@ -48,6 +48,18 @@ abstract class AbstractRepositoryGraphLike<T extends TripleLike> implements RDF4
         rdf4jTermFactory = new RDF4J(repository.getValueFactory(), salt);
     }
 
+    @Override
+    public Optional<Model> asModel() {
+        return Optional.empty();
+    }
+
+    @Override
+    public Optional<Repository> asRepository() {
+        return Optional.of(repository);
+    }
+
+    protected abstract T asTripleLike(Statement s);
+
     @Override
     public void close() throws Exception {
         if (handleInitAndShutdown) {
@@ -57,20 +69,8 @@ abstract class AbstractRepositoryGraphLike<T extends TripleLike> implements RDF4
         // down
     }
 
-    protected abstract T asTripleLike(Statement s);
-
     protected RepositoryConnection getRepositoryConnection() {
         return repository.getConnection();
     }
 
-    @Override
-    public Optional<Repository> asRepository() {
-        return Optional.of(repository);
-    }
-
-    @Override
-    public Optional<Model> asModel() {
-        return Optional.empty();
-    }
-
 }
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/BlankNodeImpl.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/BlankNodeImpl.java
index b50ccf71..2e50e2bd 100644
--- a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/BlankNodeImpl.java
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/BlankNodeImpl.java
@@ -85,13 +85,13 @@ final class BlankNodeImpl extends AbstractRDFTerm<BNode> implements RDF4JBlankNo
     }
 
     @Override
-    public String uniqueReference() {
-        final UUID uuid = new UUID(saltUUIDmost, saltUUIDleast);
-        return "urn:uuid:" + uuid + "#" + value.getID();
+    public String toString() {
+        return ntriplesString() + " [" + uniqueReference() + "]";
     }
 
     @Override
-    public String toString() {
-        return ntriplesString() + " [" + uniqueReference() + "]";
+    public String uniqueReference() {
+        final UUID uuid = new UUID(saltUUIDmost, saltUUIDleast);
+        return "urn:uuid:" + uuid + "#" + value.getID();
     }
 }
\ No newline at end of file
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ConvertedStatements.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ConvertedStatements.java
index 6d8cd578..b9add7b9 100644
--- a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ConvertedStatements.java
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ConvertedStatements.java
@@ -30,8 +30,24 @@ import org.eclipse.rdf4j.repository.RepositoryResult;
 
 final class ConvertedStatements<T> implements ClosableIterable<T> {
 
+    private final class ConvertedIterator implements Iterator<T> {
+        @Override
+        public boolean hasNext() {
+            final boolean hasNext = results.hasNext();
+            if (!hasNext) {
+                close();
+            }
+            return hasNext;
+        }
+
+        @Override
+        public T next() {
+            return statementAdapter.apply(results.next());
+        }
+    }
     private final RepositoryConnection conn;
     private final RepositoryResult<Statement> results;
+
     private final Function<Statement, T> statementAdapter;
 
     ConvertedStatements(final Supplier<RepositoryConnection> repositoryConnector, final Function<Statement, T> statementAdapter,
@@ -41,31 +57,15 @@ final class ConvertedStatements<T> implements ClosableIterable<T> {
         this.results = conn.getStatements(subj, pred, obj, contexts);
     }
 
-    @Override
-    public Iterator<T> iterator() {
-        return new ConvertedIterator();
-    }
-
     @Override
     public void close() {
         results.close();
         conn.close();
     }
 
-    private final class ConvertedIterator implements Iterator<T> {
-        @Override
-        public boolean hasNext() {
-            final boolean hasNext = results.hasNext();
-            if (!hasNext) {
-                close();
-            }
-            return hasNext;
-        }
-
-        @Override
-        public T next() {
-            return statementAdapter.apply(results.next());
-        }
+    @Override
+    public Iterator<T> iterator() {
+        return new ConvertedIterator();
     }
 
 }
\ No newline at end of file
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/LiteralImpl.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/LiteralImpl.java
index c6a90702..16f3db7c 100644
--- a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/LiteralImpl.java
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/LiteralImpl.java
@@ -29,14 +29,14 @@ final class LiteralImpl extends AbstractRDFTerm<org.eclipse.rdf4j.model.Literal>
 
     private static final String QUOTE = "\"";
 
-    LiteralImpl(final org.eclipse.rdf4j.model.Literal literal) {
-        super(literal);
-    }
-
     private static String lowerCase(final String langTag) {
         return langTag.toLowerCase(Locale.ROOT);
     }
 
+    LiteralImpl(final org.eclipse.rdf4j.model.Literal literal) {
+        super(literal);
+    }
+
     @Override
     public boolean equals(final Object obj) {
         if (obj == this) {
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ModelGraphImpl.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ModelGraphImpl.java
index 7827712c..54246101 100644
--- a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ModelGraphImpl.java
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/ModelGraphImpl.java
@@ -85,6 +85,36 @@ final class ModelGraphImpl implements RDF4JGraph {
         return model.contains(rdf4jTermFactory.asStatement(triple));
     }
 
+    @Override
+    public Set<RDF4JBlankNodeOrIRI> getContextMask() {
+        // ModelGraph always do the unionGraph
+        return Collections.emptySet();
+        // TODO: Should we support contextMask like in RepositoryGraphImpl?
+    }
+
+    @Override
+    public ClosableIterable<Triple> iterate() throws ConcurrentModificationException, IllegalStateException {
+        return iterate(null, null, null);
+    }
+
+    @Override
+    public ClosableIterable<Triple> iterate(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        return new ClosableIterable<Triple>() {
+            @Override
+            public void close() throws Exception {
+                // no-op as Model don't have transaction
+            }
+
+            @SuppressWarnings("unchecked")
+            @Override
+            public Iterator<Triple> iterator() {
+                // double-cast to fight Java generics..
+                final Stream<? extends Triple> s = stream(subject, predicate, object);
+                return (Iterator<Triple>) s.iterator();
+            }
+        };
+    }
+
     @Override
     public void remove(final BlankNodeOrIRI subject, final org.apache.commons.rdf.api.IRI predicate, final RDFTerm object) {
         model.remove((Resource) rdf4jTermFactory.asValue(subject),
@@ -120,34 +150,4 @@ final class ModelGraphImpl implements RDF4JGraph {
                 .parallelStream().map(rdf4jTermFactory::asTriple);
     }
 
-    @Override
-    public Set<RDF4JBlankNodeOrIRI> getContextMask() {
-        // ModelGraph always do the unionGraph
-        return Collections.emptySet();
-        // TODO: Should we support contextMask like in RepositoryGraphImpl?
-    }
-
-    @Override
-    public ClosableIterable<Triple> iterate(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        return new ClosableIterable<Triple>() {
-            @SuppressWarnings("unchecked")
-            @Override
-            public Iterator<Triple> iterator() {
-                // double-cast to fight Java generics..
-                final Stream<? extends Triple> s = stream(subject, predicate, object);
-                return (Iterator<Triple>) s.iterator();
-            }
-
-            @Override
-            public void close() throws Exception {
-                // no-op as Model don't have transaction
-            }
-        };
-    }
-
-    @Override
-    public ClosableIterable<Triple> iterate() throws ConcurrentModificationException, IllegalStateException {
-        return iterate(null, null, null);
-    }
-
 }
\ No newline at end of file
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryDatasetImpl.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryDatasetImpl.java
index 92fbf7e9..ed0e5cee 100644
--- a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryDatasetImpl.java
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryDatasetImpl.java
@@ -45,29 +45,42 @@ class RepositoryDatasetImpl extends AbstractRepositoryGraphLike<Quad> implements
     }
 
     @Override
-    public void add(final Quad tripleLike) {
-        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
+    public void add(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        final Resource context = (Resource) rdf4jTermFactory.asValue(graphName);
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
         try (RepositoryConnection conn = getRepositoryConnection()) {
-            conn.add(statement);
+            conn.add(subj, pred, obj, context);
             conn.commit();
         }
     }
 
     @Override
-    public boolean contains(final Quad tripleLike) {
+    public void add(final Quad tripleLike) {
         final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
         try (RepositoryConnection conn = getRepositoryConnection()) {
-            return conn.hasStatement(statement, includeInferred);
+            conn.add(statement);
+            conn.commit();
         }
     }
 
-    @Override
-    public void remove(final Quad tripleLike) {
-        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
-        try (RepositoryConnection conn = getRepositoryConnection()) {
-            conn.remove(statement);
-            conn.commit();
+    private Resource[] asContexts(final Optional<BlankNodeOrIRI> graphName) {
+        Resource[] contexts;
+        if (graphName == null) {
+            // no contexts == any contexts
+            contexts = new Resource[0];
+        } else {
+            final BlankNodeOrIRI g = graphName.orElse(null);
+            final Resource context = (Resource) rdf4jTermFactory.asValue(g);
+            contexts = new Resource[] { context };
         }
+        return contexts;
+    }
+
+    @Override
+    protected RDF4JQuad asTripleLike(final Statement s) {
+        return rdf4jTermFactory.asQuad(s);
     }
 
     @Override
@@ -79,63 +92,96 @@ class RepositoryDatasetImpl extends AbstractRepositoryGraphLike<Quad> implements
     }
 
     @Override
-    public long size() {
-        if (includeInferred) {
-            // We'll need to count them all
-            return stream().count();
+    public boolean contains(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
+        final Resource[] contexts = asContexts(graphName);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            return conn.hasStatement(subj, pred, obj, includeInferred, contexts);
         }
-        // else: Ask directly
+    }
+
+    @Override
+    public boolean contains(final Quad tripleLike) {
+        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
         try (RepositoryConnection conn = getRepositoryConnection()) {
-            return conn.size();
+            return conn.hasStatement(statement, includeInferred);
         }
     }
 
     @Override
-    public void add(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+    public Graph getGraph() {
+        // default context only
+        // NOTE: We carry over the 'salt' as the graph's BlankNode should be
+        // equal to our BlankNodes
+        return new RepositoryGraphImpl(repository, salt, false, includeInferred, (Resource) null);
+    }
+
+    @Override
+    public Optional<Graph> getGraph(final BlankNodeOrIRI graphName) {
+        // NOTE: May be null to indicate default context
         final Resource context = (Resource) rdf4jTermFactory.asValue(graphName);
+        // NOTE: We carry over the 'salt' as the graph's BlankNode should be
+        // equal to our BlankNodes
+        return Optional.of(new RepositoryGraphImpl(repository, salt, false, includeInferred, context));
+    }
+
+    @Override
+    public Stream<BlankNodeOrIRI> getGraphNames() {
+       final RepositoryConnection conn = getRepositoryConnection();
+       final RepositoryResult<Resource> contexts = conn.getContextIDs();
+        return Iterations.stream(contexts).map(g -> (BlankNodeOrIRI) rdf4jTermFactory.asRDFTerm(g))
+                .onClose(conn::close);
+    }
+
+    @Override
+    public ClosableIterable<Quad> iterate() throws ConcurrentModificationException, IllegalStateException {
+        return iterate(null, null, null, null);
+    }
+
+    @Override
+    public ClosableIterable<Quad> iterate(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate,
+            final RDFTerm object) throws ConcurrentModificationException, IllegalStateException {
+        final Resource[] contexts = asContexts(graphName);
         final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
         final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
         final Value obj = rdf4jTermFactory.asValue(object);
-        try (RepositoryConnection conn = getRepositoryConnection()) {
-            conn.add(subj, pred, obj, context);
-            conn.commit();
-        }
+        return new ConvertedStatements<>(this::getRepositoryConnection, rdf4jTermFactory::asQuad, subj, pred, obj,
+                contexts);
     }
 
     @Override
-    public boolean contains(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+    public void remove(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
         final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
         final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
         final Value obj = rdf4jTermFactory.asValue(object);
         final Resource[] contexts = asContexts(graphName);
+
         try (RepositoryConnection conn = getRepositoryConnection()) {
-            return conn.hasStatement(subj, pred, obj, includeInferred, contexts);
+            conn.remove(subj, pred, obj, contexts);
+            conn.commit();
         }
     }
 
-    private Resource[] asContexts(final Optional<BlankNodeOrIRI> graphName) {
-        Resource[] contexts;
-        if (graphName == null) {
-            // no contexts == any contexts
-            contexts = new Resource[0];
-        } else {
-            final BlankNodeOrIRI g = graphName.orElse(null);
-            final Resource context = (Resource) rdf4jTermFactory.asValue(g);
-            contexts = new Resource[] { context };
+    @Override
+    public void remove(final Quad tripleLike) {
+        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            conn.remove(statement);
+            conn.commit();
         }
-        return contexts;
     }
 
     @Override
-    public void remove(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
-        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
-        final Value obj = rdf4jTermFactory.asValue(object);
-        final Resource[] contexts = asContexts(graphName);
-
+    public long size() {
+        if (includeInferred) {
+            // We'll need to count them all
+            return stream().count();
+        }
+        // else: Ask directly
         try (RepositoryConnection conn = getRepositoryConnection()) {
-            conn.remove(subj, pred, obj, contexts);
-            conn.commit();
+            return conn.size();
         }
     }
 
@@ -174,50 +220,4 @@ class RepositoryDatasetImpl extends AbstractRepositoryGraphLike<Quad> implements
 
     }
 
-    @Override
-    public ClosableIterable<Quad> iterate() throws ConcurrentModificationException, IllegalStateException {
-        return iterate(null, null, null, null);
-    }
-
-    @Override
-    public ClosableIterable<Quad> iterate(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate,
-            final RDFTerm object) throws ConcurrentModificationException, IllegalStateException {
-        final Resource[] contexts = asContexts(graphName);
-        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
-        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
-        final Value obj = rdf4jTermFactory.asValue(object);
-        return new ConvertedStatements<>(this::getRepositoryConnection, rdf4jTermFactory::asQuad, subj, pred, obj,
-                contexts);
-    }
-
-    @Override
-    protected RDF4JQuad asTripleLike(final Statement s) {
-        return rdf4jTermFactory.asQuad(s);
-    }
-
-    @Override
-    public Graph getGraph() {
-        // default context only
-        // NOTE: We carry over the 'salt' as the graph's BlankNode should be
-        // equal to our BlankNodes
-        return new RepositoryGraphImpl(repository, salt, false, includeInferred, (Resource) null);
-    }
-
-    @Override
-    public Optional<Graph> getGraph(final BlankNodeOrIRI graphName) {
-        // NOTE: May be null to indicate default context
-        final Resource context = (Resource) rdf4jTermFactory.asValue(graphName);
-        // NOTE: We carry over the 'salt' as the graph's BlankNode should be
-        // equal to our BlankNodes
-        return Optional.of(new RepositoryGraphImpl(repository, salt, false, includeInferred, context));
-    }
-
-    @Override
-    public Stream<BlankNodeOrIRI> getGraphNames() {
-       final RepositoryConnection conn = getRepositoryConnection();
-       final RepositoryResult<Resource> contexts = conn.getContextIDs();
-        return Iterations.stream(contexts).map(g -> (BlankNodeOrIRI) rdf4jTermFactory.asRDFTerm(g))
-                .onClose(conn::close);
-    }
-
 }
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryGraphImpl.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryGraphImpl.java
index ccfa5797..e52e9fa0 100644
--- a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryGraphImpl.java
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/RepositoryGraphImpl.java
@@ -52,29 +52,28 @@ class RepositoryGraphImpl extends AbstractRepositoryGraphLike<Triple> implements
     }
 
     @Override
-    public void add(final Triple tripleLike) {
-        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
+    public void add(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
         try (RepositoryConnection conn = getRepositoryConnection()) {
-            conn.add(statement, contextMask);
+            conn.add(subj, pred, obj, contextMask);
             conn.commit();
         }
     }
 
     @Override
-    public boolean contains(final Triple tripleLike) {
+    public void add(final Triple tripleLike) {
         final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
         try (RepositoryConnection conn = getRepositoryConnection()) {
-            return conn.hasStatement(statement, includeInferred, contextMask);
+            conn.add(statement, contextMask);
+            conn.commit();
         }
     }
 
     @Override
-    public void remove(final Triple tripleLike) {
-        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
-        try (RepositoryConnection conn = getRepositoryConnection()) {
-            conn.remove(statement, contextMask);
-            conn.commit();
-        }
+    protected RDF4JTriple asTripleLike(final Statement statement) {
+        return rdf4jTermFactory.asTriple(statement);
     }
 
     @Override
@@ -86,47 +85,30 @@ class RepositoryGraphImpl extends AbstractRepositoryGraphLike<Triple> implements
     }
 
     @Override
-    public long size() {
-        if (!includeInferred && contextMask.length == 0) {
-            try (RepositoryConnection conn = getRepositoryConnection()) {
-                return conn.size();
-            }
-        }
-        try (Stream<RDF4JTriple> stream = stream()) {
-            return stream.count();
-        }
-    }
-
-    @Override
-    public void add(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+    public boolean contains(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
         final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
         final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
         final Value obj = rdf4jTermFactory.asValue(object);
         try (RepositoryConnection conn = getRepositoryConnection()) {
-            conn.add(subj, pred, obj, contextMask);
-            conn.commit();
+            return conn.hasStatement(subj, pred, obj, includeInferred, contextMask);
         }
     }
 
     @Override
-    public boolean contains(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
-        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
-        final Value obj = rdf4jTermFactory.asValue(object);
+    public boolean contains(final Triple tripleLike) {
+        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
         try (RepositoryConnection conn = getRepositoryConnection()) {
-            return conn.hasStatement(subj, pred, obj, includeInferred, contextMask);
+            return conn.hasStatement(statement, includeInferred, contextMask);
         }
     }
 
     @Override
-    public void remove(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
-        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
-        final Value obj = rdf4jTermFactory.asValue(object);
-        try (RepositoryConnection conn = getRepositoryConnection()) {
-            conn.remove(subj, pred, obj, contextMask);
-            conn.commit();
+    public Set<RDF4JBlankNodeOrIRI> getContextMask() {
+        final Set<RDF4JBlankNodeOrIRI> mask = new HashSet<>();
+        for (final Resource s : contextMask) {
+            mask.add(rdf4jTermFactory.asRDFTerm(s));
         }
+        return Collections.unmodifiableSet(mask);
     }
 
     @Override
@@ -144,6 +126,38 @@ class RepositoryGraphImpl extends AbstractRepositoryGraphLike<Triple> implements
                 obj, contextMask);
     }
 
+    @Override
+    public void remove(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        final Resource subj = (Resource) rdf4jTermFactory.asValue(subject);
+        final org.eclipse.rdf4j.model.IRI pred = (org.eclipse.rdf4j.model.IRI) rdf4jTermFactory.asValue(predicate);
+        final Value obj = rdf4jTermFactory.asValue(object);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            conn.remove(subj, pred, obj, contextMask);
+            conn.commit();
+        }
+    }
+
+    @Override
+    public void remove(final Triple tripleLike) {
+        final Statement statement = rdf4jTermFactory.asStatement(tripleLike);
+        try (RepositoryConnection conn = getRepositoryConnection()) {
+            conn.remove(statement, contextMask);
+            conn.commit();
+        }
+    }
+
+    @Override
+    public long size() {
+        if (!includeInferred && contextMask.length == 0) {
+            try (RepositoryConnection conn = getRepositoryConnection()) {
+                return conn.size();
+            }
+        }
+        try (Stream<RDF4JTriple> stream = stream()) {
+            return stream.count();
+        }
+    }
+
     @Override
     public Stream<RDF4JTriple> stream() {
         return stream(null, null, null);
@@ -176,18 +190,4 @@ class RepositoryGraphImpl extends AbstractRepositoryGraphLike<Triple> implements
         return stream == null ? null : stream.onClose(conn::close);
     }
 
-    @Override
-    protected RDF4JTriple asTripleLike(final Statement statement) {
-        return rdf4jTermFactory.asTriple(statement);
-    }
-
-    @Override
-    public Set<RDF4JBlankNodeOrIRI> getContextMask() {
-        final Set<RDF4JBlankNodeOrIRI> mask = new HashSet<>();
-        for (final Resource s : contextMask) {
-            mask.add(rdf4jTermFactory.asRDFTerm(s));
-        }
-        return Collections.unmodifiableSet(mask);
-    }
-
 }
diff --git a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/BlankNodeImpl.java b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/BlankNodeImpl.java
index f751f476..fd218074 100644
--- a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/BlankNodeImpl.java
+++ b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/BlankNodeImpl.java
@@ -67,26 +67,6 @@ final class BlankNodeImpl implements BlankNode, SimpleRDFTerm {
         this.uniqueReference = UUID.nameUUIDFromBytes(uuidInput.getBytes(StandardCharsets.UTF_8)).toString();
     }
 
-    @Override
-    public String uniqueReference() {
-        return uniqueReference;
-    }
-
-    @Override
-    public String ntriplesString() {
-        return "_:" + uniqueReference;
-    }
-
-    @Override
-    public String toString() {
-        return ntriplesString();
-    }
-
-    @Override
-    public int hashCode() {
-        return uniqueReference.hashCode();
-    }
-
     @Override
     public boolean equals(final Object obj) {
         if (this == obj) {
@@ -106,4 +86,24 @@ final class BlankNodeImpl implements BlankNode, SimpleRDFTerm {
         return true;
     }
 
+    @Override
+    public int hashCode() {
+        return uniqueReference.hashCode();
+    }
+
+    @Override
+    public String ntriplesString() {
+        return "_:" + uniqueReference;
+    }
+
+    @Override
+    public String toString() {
+        return ntriplesString();
+    }
+
+    @Override
+    public String uniqueReference() {
+        return uniqueReference;
+    }
+
 }
diff --git a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/DatasetGraphView.java b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/DatasetGraphView.java
index e4f860cc..88ee5e16 100644
--- a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/DatasetGraphView.java
+++ b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/DatasetGraphView.java
@@ -78,9 +78,8 @@ public class DatasetGraphView implements Graph {
     }
 
     @Override
-    public void close() throws Exception {
-        dataset.close();
-
+    public void add(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        dataset.add(namedGraph, subject, predicate, object);
     }
 
     @Override
@@ -89,20 +88,14 @@ public class DatasetGraphView implements Graph {
     }
 
     @Override
-    public void add(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        dataset.add(namedGraph, subject, predicate, object);
+    public void clear() {
+        dataset.remove(unionOrNamedGraph(), null, null, null);
     }
 
     @Override
-    public boolean contains(final Triple triple) {
-        return dataset.contains(unionOrNamedGraph(), triple.getSubject(), triple.getPredicate(), triple.getObject());
-    }
+    public void close() throws Exception {
+        dataset.close();
 
-    private Optional<BlankNodeOrIRI> unionOrNamedGraph() {
-        if (unionGraph) {
-            return null;
-        }
-        return Optional.ofNullable(namedGraph);
     }
 
     @Override
@@ -111,8 +104,8 @@ public class DatasetGraphView implements Graph {
     }
 
     @Override
-    public void remove(final Triple triple) {
-        dataset.remove(unionOrNamedGraph(), triple.getSubject(), triple.getPredicate(), triple.getObject());
+    public boolean contains(final Triple triple) {
+        return dataset.contains(unionOrNamedGraph(), triple.getSubject(), triple.getPredicate(), triple.getObject());
     }
 
     @Override
@@ -121,8 +114,8 @@ public class DatasetGraphView implements Graph {
     }
 
     @Override
-    public void clear() {
-        dataset.remove(unionOrNamedGraph(), null, null, null);
+    public void remove(final Triple triple) {
+        dataset.remove(unionOrNamedGraph(), triple.getSubject(), triple.getPredicate(), triple.getObject());
     }
 
     @Override
@@ -145,4 +138,11 @@ public class DatasetGraphView implements Graph {
         return stream;
     }
 
+    private Optional<BlankNodeOrIRI> unionOrNamedGraph() {
+        if (unionGraph) {
+            return null;
+        }
+        return Optional.ofNullable(namedGraph);
+    }
+
 }
diff --git a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/DatasetImpl.java b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/DatasetImpl.java
index a8c04553..1f13062e 100644
--- a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/DatasetImpl.java
+++ b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/DatasetImpl.java
@@ -80,6 +80,45 @@ final class DatasetImpl implements Dataset {
         }
     }
 
+    @Override
+    public void clear() {
+        quads.clear();
+    }
+
+    @Override
+    public void close() {
+    }
+
+    @Override
+    public boolean contains(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        return stream(graphName, subject, predicate, object).findAny().isPresent();
+    }
+
+    @Override
+    public boolean contains(final Quad quad) {
+        return quads.contains(Objects.requireNonNull(quad));
+    }
+
+    @Override
+    public Graph getGraph() {
+        return getGraph(null).get();
+    }
+
+    @Override
+    public Optional<Graph> getGraph(final BlankNodeOrIRI graphName) {
+        return Optional.of(new DatasetGraphView(this, graphName));
+    }
+
+    @Override
+    public Stream<BlankNodeOrIRI> getGraphNames() {
+        // Not very efficient..
+        return stream().map(Quad::getGraphName).filter(Optional::isPresent).map(Optional::get).distinct();
+    }
+
+    private Stream<Quad> getQuads(final Predicate<Quad> filter) {
+        return stream().filter(filter);
+    }
+
     private <T extends RDFTerm> RDFTerm internallyMap(final T object) {
         if (object == null || object instanceof SimpleRDFTerm) {
             return object;
@@ -107,18 +146,22 @@ final class DatasetImpl implements Dataset {
     }
 
     @Override
-    public void clear() {
-        quads.clear();
+    public void remove(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        final Stream<Quad> toRemove = stream(graphName, subject, predicate, object);
+        for (final Quad t : toRemove.collect(Collectors.toList())) {
+            // Avoid ConcurrentModificationException in ArrayList
+            remove(t);
+        }
     }
 
     @Override
-    public boolean contains(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        return stream(graphName, subject, predicate, object).findAny().isPresent();
+    public void remove(final Quad quad) {
+        quads.remove(Objects.requireNonNull(quad));
     }
 
     @Override
-    public boolean contains(final Quad quad) {
-        return quads.contains(Objects.requireNonNull(quad));
+    public long size() {
+        return quads.size();
     }
 
     @Override
@@ -158,29 +201,6 @@ final class DatasetImpl implements Dataset {
         });
     }
 
-    private Stream<Quad> getQuads(final Predicate<Quad> filter) {
-        return stream().filter(filter);
-    }
-
-    @Override
-    public void remove(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        final Stream<Quad> toRemove = stream(graphName, subject, predicate, object);
-        for (final Quad t : toRemove.collect(Collectors.toList())) {
-            // Avoid ConcurrentModificationException in ArrayList
-            remove(t);
-        }
-    }
-
-    @Override
-    public void remove(final Quad quad) {
-        quads.remove(Objects.requireNonNull(quad));
-    }
-
-    @Override
-    public long size() {
-        return quads.size();
-    }
-
     @Override
     public String toString() {
         final String s = stream().limit(TO_STRING_MAX).map(Object::toString).collect(Collectors.joining("\n"));
@@ -190,24 +210,4 @@ final class DatasetImpl implements Dataset {
         return s;
     }
 
-    @Override
-    public void close() {
-    }
-
-    @Override
-    public Graph getGraph() {
-        return getGraph(null).get();
-    }
-
-    @Override
-    public Optional<Graph> getGraph(final BlankNodeOrIRI graphName) {
-        return Optional.of(new DatasetGraphView(this, graphName));
-    }
-
-    @Override
-    public Stream<BlankNodeOrIRI> getGraphNames() {
-        // Not very efficient..
-        return stream().map(Quad::getGraphName).filter(Optional::isPresent).map(Optional::get).distinct();
-    }
-
 }
diff --git a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/GraphImpl.java b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/GraphImpl.java
index ac4f7dc9..89472d56 100644
--- a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/GraphImpl.java
+++ b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/GraphImpl.java
@@ -63,6 +63,25 @@ final class GraphImpl implements Graph {
         triples.add(internallyMap(triple));
     }
 
+    @Override
+    public void clear() {
+        triples.clear();
+    }
+
+    @Override
+    public boolean contains(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        return stream(subject, predicate, object).findFirst().isPresent();
+    }
+
+    @Override
+    public boolean contains(final Triple triple) {
+        return triples.contains(internallyMap(triple));
+    }
+
+    private Stream<Triple> getTriples(final Predicate<Triple> filter) {
+        return stream().filter(filter);
+    }
+
     private <T extends RDFTerm> RDFTerm internallyMap(final T object) {
         if (object == null || object instanceof SimpleRDFTerm) {
             // No need to re-map our own objects.
@@ -107,18 +126,22 @@ final class GraphImpl implements Graph {
     }
 
     @Override
-    public void clear() {
-        triples.clear();
+    public void remove(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        final Stream<Triple> toRemove = stream(subject, predicate, object);
+        for (final Triple t : toRemove.collect(Collectors.toList())) {
+            // Avoid ConcurrentModificationException in ArrayList
+            remove(t);
+        }
     }
 
     @Override
-    public boolean contains(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        return stream(subject, predicate, object).findFirst().isPresent();
+    public void remove(final Triple triple) {
+        triples.remove(internallyMap(triple));
     }
 
     @Override
-    public boolean contains(final Triple triple) {
-        return triples.contains(internallyMap(triple));
+    public long size() {
+        return triples.size();
     }
 
     @Override
@@ -148,29 +171,6 @@ final class GraphImpl implements Graph {
         });
     }
 
-    private Stream<Triple> getTriples(final Predicate<Triple> filter) {
-        return stream().filter(filter);
-    }
-
-    @Override
-    public void remove(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        final Stream<Triple> toRemove = stream(subject, predicate, object);
-        for (final Triple t : toRemove.collect(Collectors.toList())) {
-            // Avoid ConcurrentModificationException in ArrayList
-            remove(t);
-        }
-    }
-
-    @Override
-    public void remove(final Triple triple) {
-        triples.remove(internallyMap(triple));
-    }
-
-    @Override
-    public long size() {
-        return triples.size();
-    }
-
     @Override
     public String toString() {
         final String s = stream().limit(TO_STRING_MAX).map(Object::toString).collect(Collectors.joining("\n"));
diff --git a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/IRIImpl.java b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/IRIImpl.java
index ebdbf7bb..d717d6f3 100644
--- a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/IRIImpl.java
+++ b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/IRIImpl.java
@@ -36,21 +36,6 @@ final class IRIImpl implements IRI, SimpleRDF.SimpleRDFTerm {
         this.iri = iri;
     }
 
-    @Override
-    public String getIRIString() {
-        return iri;
-    }
-
-    @Override
-    public String ntriplesString() {
-        return "<" + getIRIString() + ">";
-    }
-
-    @Override
-    public String toString() {
-        return ntriplesString();
-    }
-
     @Override
     public boolean equals(final Object obj) {
         if (this == obj) {
@@ -63,9 +48,24 @@ final class IRIImpl implements IRI, SimpleRDF.SimpleRDFTerm {
         return getIRIString().equals(other.getIRIString());
     }
 
+    @Override
+    public String getIRIString() {
+        return iri;
+    }
+
     @Override
     public int hashCode() {
         return iri.hashCode();
     }
 
+    @Override
+    public String ntriplesString() {
+        return "<" + getIRIString() + ">";
+    }
+
+    @Override
+    public String toString() {
+        return ntriplesString();
+    }
+
 }
diff --git a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/LiteralImpl.java b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/LiteralImpl.java
index 508b9606..7de4bcca 100644
--- a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/LiteralImpl.java
+++ b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/LiteralImpl.java
@@ -32,8 +32,12 @@ final class LiteralImpl implements Literal, SimpleRDF.SimpleRDFTerm {
 
     private static final String QUOTE = "\"";
 
+    private static String lowerCase(final String langTag) {
+        return langTag.toLowerCase(Locale.ROOT);
+    }
     private final IRI dataType;
     private final String languageTag;
+
     private final String lexicalForm;
 
     public LiteralImpl(final String literal) {
@@ -68,6 +72,19 @@ final class LiteralImpl implements Literal, SimpleRDF.SimpleRDFTerm {
         this.dataType = Types.RDF_LANGSTRING;
     }
 
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || !(obj instanceof Literal)) {
+            return false;
+        }
+        final Literal literal = (Literal) obj;
+        return getDatatype().equals(literal.getDatatype()) && getLexicalForm().equals(literal.getLexicalForm())
+                && getLanguageTag().equals(literal.getLanguageTag().map(LiteralImpl::lowerCase));
+    }
+
     @Override
     public IRI getDatatype() {
         return dataType;
@@ -83,6 +100,11 @@ final class LiteralImpl implements Literal, SimpleRDF.SimpleRDFTerm {
         return lexicalForm;
     }
 
+    @Override
+    public int hashCode() {
+        return Objects.hash(lexicalForm, dataType, languageTag);
+    }
+
     @Override
     public String ntriplesString() {
         final StringBuilder sb = new StringBuilder();
@@ -111,26 +133,4 @@ final class LiteralImpl implements Literal, SimpleRDF.SimpleRDFTerm {
         return ntriplesString();
     }
 
-    @Override
-    public int hashCode() {
-        return Objects.hash(lexicalForm, dataType, languageTag);
-    }
-
-    private static String lowerCase(final String langTag) {
-        return langTag.toLowerCase(Locale.ROOT);
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null || !(obj instanceof Literal)) {
-            return false;
-        }
-        final Literal literal = (Literal) obj;
-        return getDatatype().equals(literal.getDatatype()) && getLexicalForm().equals(literal.getLexicalForm())
-                && getLanguageTag().equals(literal.getLanguageTag().map(LiteralImpl::lowerCase));
-    }
-
 }
diff --git a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/QuadImpl.java b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/QuadImpl.java
index c8815592..658a2839 100644
--- a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/QuadImpl.java
+++ b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/QuadImpl.java
@@ -59,18 +59,23 @@ final class QuadImpl implements Quad {
     }
 
     @Override
-    public Optional<BlankNodeOrIRI> getGraphName() {
-        return Optional.ofNullable(graphName);
+    public Triple asTriple() {
+        return new TripleImpl(getSubject(), getPredicate(), getObject());
     }
 
     @Override
-    public BlankNodeOrIRI getSubject() {
-        return subject;
+    public boolean equals(final Object obj) {
+        if (!(obj instanceof Quad)) {
+            return false;
+        }
+        final Quad other = (Quad) obj;
+        return getGraphName().equals(other.getGraphName()) && getSubject().equals(other.getSubject())
+                && getPredicate().equals(other.getPredicate()) && getObject().equals(other.getObject());
     }
 
     @Override
-    public IRI getPredicate() {
-        return predicate;
+    public Optional<BlankNodeOrIRI> getGraphName() {
+        return Optional.ofNullable(graphName);
     }
 
     @Override
@@ -79,30 +84,25 @@ final class QuadImpl implements Quad {
     }
 
     @Override
-    public String toString() {
-        return getSubject().ntriplesString() + " " + getPredicate().ntriplesString() + " "
-                + getObject().ntriplesString() + " " + getGraphName().map(g -> g.ntriplesString() + " ").orElse("")
-                + ".";
+    public IRI getPredicate() {
+        return predicate;
     }
 
     @Override
-    public int hashCode() {
-        return Objects.hash(subject, predicate, object, graphName);
+    public BlankNodeOrIRI getSubject() {
+        return subject;
     }
 
     @Override
-    public boolean equals(final Object obj) {
-        if (!(obj instanceof Quad)) {
-            return false;
-        }
-        final Quad other = (Quad) obj;
-        return getGraphName().equals(other.getGraphName()) && getSubject().equals(other.getSubject())
-                && getPredicate().equals(other.getPredicate()) && getObject().equals(other.getObject());
+    public int hashCode() {
+        return Objects.hash(subject, predicate, object, graphName);
     }
 
     @Override
-    public Triple asTriple() {
-        return new TripleImpl(getSubject(), getPredicate(), getObject());
+    public String toString() {
+        return getSubject().ntriplesString() + " " + getPredicate().ntriplesString() + " "
+                + getObject().ntriplesString() + " " + getGraphName().map(g -> g.ntriplesString() + " ").orElse("")
+                + ".";
     }
 
 }
diff --git a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/SimpleRDF.java b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/SimpleRDF.java
index 28af6fb4..f9b7f18d 100644
--- a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/SimpleRDF.java
+++ b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/SimpleRDF.java
@@ -65,6 +65,11 @@ public class SimpleRDF implements RDF {
         return new BlankNodeImpl(SALT, name);
     }
 
+    @Override
+    public Dataset createDataset() throws UnsupportedOperationException {
+        return new DatasetImpl(this);
+    }
+
     @Override
     public Graph createGraph() {
         // Creates a GraphImpl object using this object as the factory for
@@ -72,11 +77,6 @@ public class SimpleRDF implements RDF {
         return new GraphImpl(this);
     }
 
-    @Override
-    public Dataset createDataset() throws UnsupportedOperationException {
-        return new DatasetImpl(this);
-    }
-
     @Override
     public IRI createIRI(final String iri) {
         final IRI result = new IRIImpl(iri);
@@ -99,14 +99,14 @@ public class SimpleRDF implements RDF {
         return new LiteralImpl(literal, language);
     }
 
-    @Override
-    public Triple createTriple(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
-        return new TripleImpl(subject, predicate, object);
-    }
-
     @Override
     public Quad createQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object)
             throws IllegalArgumentException {
         return new QuadImpl(graphName, subject, predicate, object);
     }
+
+    @Override
+    public Triple createTriple(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
+        return new TripleImpl(subject, predicate, object);
+    }
 }
diff --git a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/TripleImpl.java b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/TripleImpl.java
index f7a9eeb9..f2a846a6 100644
--- a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/TripleImpl.java
+++ b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/TripleImpl.java
@@ -53,24 +53,28 @@ final class TripleImpl implements Triple {
     }
 
     @Override
-    public BlankNodeOrIRI getSubject() {
-        return subject;
+    public boolean equals(final Object obj) {
+        if (!(obj instanceof Triple)) {
+            return false;
+        }
+        final Triple other = (Triple) obj;
+        return getSubject().equals(other.getSubject()) && getPredicate().equals(other.getPredicate())
+                && getObject().equals(other.getObject());
     }
 
     @Override
-    public IRI getPredicate() {
-        return predicate;
+    public RDFTerm getObject() {
+        return object;
     }
 
     @Override
-    public RDFTerm getObject() {
-        return object;
+    public IRI getPredicate() {
+        return predicate;
     }
 
     @Override
-    public String toString() {
-        return getSubject().ntriplesString() + " " + getPredicate().ntriplesString() + " "
-                + getObject().ntriplesString() + " .";
+    public BlankNodeOrIRI getSubject() {
+        return subject;
     }
 
     @Override
@@ -79,13 +83,9 @@ final class TripleImpl implements Triple {
     }
 
     @Override
-    public boolean equals(final Object obj) {
-        if (!(obj instanceof Triple)) {
-            return false;
-        }
-        final Triple other = (Triple) obj;
-        return getSubject().equals(other.getSubject()) && getPredicate().equals(other.getPredicate())
-                && getObject().equals(other.getObject());
+    public String toString() {
+        return getSubject().ntriplesString() + " " + getPredicate().ntriplesString() + " "
+                + getObject().ntriplesString() + " .";
     }
 
 }
diff --git a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/Types.java b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/Types.java
index 67c3acde..2c029492 100644
--- a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/Types.java
+++ b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/Types.java
@@ -285,6 +285,38 @@ public final class Types implements IRI, SimpleRDF.SimpleRDFTerm {
         ALL_TYPES = Collections.unmodifiableSet(tempTypes);
     }
 
+    /**
+     * Gets the IRI from this collection if it is present, or return
+     * {@link Optional#empty()} otherwise.
+     *
+     * @param nextIRI
+     *            The IRI to look for.
+     * @return An {@link Optional} containing the IRI from this collection or
+     *         {@link Optional#empty()} if it is not present here.
+     */
+    public static Optional<IRI> get(final IRI nextIRI) {
+        if (ALL_TYPES.contains(nextIRI)) {
+            // If we know about this IRI, then look through our set to find the
+            // object that matches and return it
+            for (final IRI nextType : ALL_TYPES) {
+                if (nextType.equals(nextIRI)) {
+                    return Optional.of(nextType);
+                }
+            }
+        }
+        return Optional.empty();
+    }
+
+    /**
+     * Gets an immutable set of the IRIs used by the RDF-1.1 specification to
+     * define types, from the RDF and XML Schema vocabularies.
+     *
+     * @return A {@link Set} containing all of the IRIs in this collection.
+     */
+    public static Set<IRI> values() {
+        return ALL_TYPES;
+    }
+
     private final IRI field;
 
     private Types(final String field) {
@@ -292,59 +324,27 @@ public final class Types implements IRI, SimpleRDF.SimpleRDFTerm {
     }
 
     @Override
-    public String getIRIString() {
-        return this.field.getIRIString();
+    public boolean equals(final Object other) {
+        return this.field.equals(other);
     }
 
     @Override
-    public String ntriplesString() {
-        return this.field.ntriplesString();
+    public String getIRIString() {
+        return this.field.getIRIString();
     }
 
     @Override
-    public boolean equals(final Object other) {
-        return this.field.equals(other);
+    public int hashCode() {
+        return this.field.hashCode();
     }
 
     @Override
-    public int hashCode() {
-        return this.field.hashCode();
+    public String ntriplesString() {
+        return this.field.ntriplesString();
     }
 
     @Override
     public String toString() {
         return this.field.toString();
     }
-
-    /**
-     * Gets an immutable set of the IRIs used by the RDF-1.1 specification to
-     * define types, from the RDF and XML Schema vocabularies.
-     *
-     * @return A {@link Set} containing all of the IRIs in this collection.
-     */
-    public static Set<IRI> values() {
-        return ALL_TYPES;
-    }
-
-    /**
-     * Gets the IRI from this collection if it is present, or return
-     * {@link Optional#empty()} otherwise.
-     *
-     * @param nextIRI
-     *            The IRI to look for.
-     * @return An {@link Optional} containing the IRI from this collection or
-     *         {@link Optional#empty()} if it is not present here.
-     */
-    public static Optional<IRI> get(final IRI nextIRI) {
-        if (ALL_TYPES.contains(nextIRI)) {
-            // If we know about this IRI, then look through our set to find the
-            // object that matches and return it
-            for (final IRI nextType : ALL_TYPES) {
-                if (nextType.equals(nextIRI)) {
-                    return Optional.of(nextType);
-                }
-            }
-        }
-        return Optional.empty();
-    }
 }
diff --git a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/experimental/AbstractRDFParser.java b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/experimental/AbstractRDFParser.java
index 3dd24dac..37bb50a6 100644
--- a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/experimental/AbstractRDFParser.java
+++ b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/experimental/AbstractRDFParser.java
@@ -68,155 +68,166 @@ public abstract class AbstractRDFParser<T extends AbstractRDFParser<T>> implemen
     private static final RDF internalRdfTermFactory = new SimpleRDF();
 
     /**
-     * Gets the set {@link RDF}, if any.
-     *
-     * @return The {@link RDF} to use, or {@link Optional#empty()} if it has not
-     *         been set
-     */
-    public Optional<RDF> getRdfTermFactory() {
-        return Optional.ofNullable(rdfTermFactory);
-    }
-
-    /**
-     * Gets the set content-type {@link RDFSyntax}, if any.
+     * Return the file extension of a Path - if any.
      * <p>
-     * If this is {@link Optional#isPresent()}, then {@link #getContentType()}
-     * contains the value of {@link RDFSyntax#mediaType}.
-     *
-     * @return The {@link RDFSyntax} of the content type, or
-     *         {@link Optional#empty()} if it has not been set
-     */
-    public Optional<RDFSyntax> getContentTypeSyntax() {
-        return Optional.ofNullable(contentTypeSyntax);
-    }
-
-    /**
-     * Gets the set content-type String, if any.
+     * The returned file extension includes the leading <code>.</code>
      * <p>
-     * If this is {@link Optional#isPresent()} and is recognized by
-     * {@link RDFSyntax#byMediaType(String)}, then the corresponding
-     * {@link RDFSyntax} is set on {@link #getContentType()}, otherwise that is
-     * {@link Optional#empty()}.
+     * Note that this only returns the last extension, e.g. the file extension
+     * for <code>archive.tar.gz</code> would be <code>.gz</code>
      *
-     * @return The Content-Type IANA media type, e.g. <code>text/turtle</code>,
-     *         or {@link Optional#empty()} if it has not been set
+     * @param path
+     *            Path which file name might contain an extension
+     * @return File extension (including the leading <code>.</code>, or
+     *         {@link Optional#empty()} if the path has no extension
      */
-    public final Optional<String> getContentType() {
-        return Optional.ofNullable(contentType);
+    private static Optional<String> fileExtension(final Path path) {
+        final Path fileName = path.getFileName();
+        if (fileName == null) {
+            return Optional.empty();
+        }
+        final String fileNameStr = fileName.toString();
+        final int last = fileNameStr.lastIndexOf(".");
+        if (last > -1) {
+            return Optional.of(fileNameStr.substring(last));
+        }
+        return Optional.empty();
     }
 
     /**
-     * Gets the target to consume parsed Quads.
+     * Guess RDFSyntax from a local file's extension.
      * <p>
-     * From the call to {@link #parseSynchronusly()}, this will be a
-     * non-<code>null</code> value (as a target is a required setting).
-     *
-     * @return The target consumer of {@link Quad}s, or <code>null</code> if it
-     *         has not yet been set.
+     * This method can be used by subclasses if {@link #getContentType()} is not
+     * present and {@link #getSourceFile()} is set.
      *
+     * @param path
+     *            Path which extension should be checked
+     * @return The {@link RDFSyntax} which has a matching
+     *         {@link RDFSyntax#fileExtension}, otherwise
+     *         {@link Optional#empty()}.
      */
-    public Consumer<Quad> getTarget() {
-        return target;
+    protected static Optional<RDFSyntax> guessRDFSyntax(final Path path) {
+        return fileExtension(path).flatMap(RDFSyntax::byFileExtension);
     }
 
+    private RDF rdfTermFactory;
+
+    private RDFSyntax contentTypeSyntax;
+
+    private String contentType;
+
+    private IRI base;
+
+    private InputStream sourceInputStream;
+
+    private Path sourceFile;
+
+    private IRI sourceIri;
+
+    private Consumer<Quad> target;
+
+    private Dataset targetDataset;
+    private Graph targetGraph;
     /**
-     * Gets the target dataset as set by {@link #target(Dataset)}.
-     * <p>
-     * The return value is {@link Optional#isPresent()} if and only if
-     * {@link #target(Dataset)} has been set, meaning that the implementation
-     * may choose to append parsed quads to the {@link Dataset} directly instead
-     * of relying on the generated {@link #getTarget()} consumer.
-     * <p>
-     * If this value is present, then {@link #getTargetGraph()} MUST be
-     * {@link Optional#empty()}.
-     *
-     * @return The target Dataset, or {@link Optional#empty()} if another kind
-     *         of target has been set.
+     * Returns this.
+     * @return this.
      */
-    public Optional<Dataset> getTargetDataset() {
-        return Optional.ofNullable(targetDataset);
+    @SuppressWarnings("unchecked")
+    protected T asT() {
+        return (T) this;
+    }
+    @Override
+    public T base(final IRI base) {
+        final AbstractRDFParser<T> c = clone();
+        c.base = base;
+        c.getBase().ifPresent(this::checkIsAbsolute);
+        return c.asT();
+    }
+    @Override
+    public T base(final String base) throws IllegalArgumentException {
+        return base(internalRdfTermFactory.createIRI(base));
     }
-
     /**
-     * Gets the target graph as set by {@link #target(Graph)}.
-     * <p>
-     * The return value is {@link Optional#isPresent()} if and only if
-     * {@link #target(Graph)} has been set, meaning that the implementation may
-     * choose to append parsed triples to the {@link Graph} directly instead of
-     * relying on the generated {@link #getTarget()} consumer.
-     * <p>
-     * If this value is present, then {@link #getTargetDataset()} MUST be
-     * {@link Optional#empty()}.
+     * Check if base is required.
      *
-     * @return The target Graph, or {@link Optional#empty()} if another kind of
-     *         target has been set.
+     * @throws IllegalStateException
+     *             if base is required, but not set.
      */
-    public Optional<Graph> getTargetGraph() {
-        return Optional.ofNullable(targetGraph);
+    protected void checkBaseRequired() throws IllegalStateException {
+        if (!getBase().isPresent() && getSourceInputStream().isPresent()
+                && !getContentTypeSyntax().filter(t -> t == RDFSyntax.NQUADS || t == RDFSyntax.NTRIPLES).isPresent()) {
+            throw new IllegalStateException("base iri required for inputstream source");
+        }
     }
-
     /**
-     * Gets the set base {@link IRI}, if present.
+     * Subclasses can override this method to check compatibility with the
+     * contentType setting.
      *
-     * @return The base {@link IRI}, or {@link Optional#empty()} if it has not
-     *         been set
+     * @throws IllegalStateException
+     *             if the {@link #getContentType()} or
+     *             {@link #getContentTypeSyntax()} is not compatible or invalid
      */
-    public Optional<IRI> getBase() {
-        return Optional.ofNullable(base);
+    protected void checkContentType() throws IllegalStateException {
     }
-
     /**
-     * Gets the set source {@link InputStream}.
+     * Check if an iri is absolute.
      * <p>
-     * If this is {@link Optional#isPresent()}, then {@link #getSourceFile()}
-     * and {@link #getSourceIri()} are {@link Optional#empty()}.
+     * Used by {@link #source(String)} and {@link #base(String)}.
      *
-     * @return The source {@link InputStream}, or {@link Optional#empty()} if it
-     *         has not been set
+     * @param iri
+     *            IRI to check
+     * @throws IllegalArgumentException
+     *             If the IRI is not absolute
      */
-    public Optional<InputStream> getSourceInputStream() {
-        return Optional.ofNullable(sourceInputStream);
+    protected void checkIsAbsolute(final IRI iri) throws IllegalArgumentException {
+        if (!URI.create(iri.getIRIString()).isAbsolute()) {
+            throw new IllegalArgumentException("IRI is not absolute: " + iri);
+        }
     }
-
     /**
-     * Gets the set source {@link Path}.
+     * Check that one and only one source is present and valid.
      * <p>
-     * If this is {@link Optional#isPresent()}, then
-     * {@link #getSourceInputStream()} and {@link #getSourceIri()} are
-     * {@link Optional#empty()}.
+     * Used by {@link #parse()}.
+     * <p>
+     * Subclasses might override this method, e.g. to support other source
+     * combinations, or to check if the sourceIri is resolvable.
      *
-     * @return The source {@link Path}, or {@link Optional#empty()} if it has
-     *         not been set
+     * @throws IOException
+     *             If a source file can't be read
      */
-    public Optional<Path> getSourceFile() {
-        return Optional.ofNullable(sourceFile);
+    protected void checkSource() throws IOException {
+        if (!getSourceFile().isPresent() && !getSourceInputStream().isPresent() && !getSourceIri().isPresent()) {
+            throw new IllegalStateException("No source has been set");
+        }
+        if (getSourceIri().isPresent() && getSourceInputStream().isPresent()) {
+            throw new IllegalStateException("Both sourceIri and sourceInputStream have been set");
+        }
+        if (getSourceIri().isPresent() && getSourceFile().isPresent()) {
+            throw new IllegalStateException("Both sourceIri and sourceFile have been set");
+        }
+        if (getSourceInputStream().isPresent() && getSourceFile().isPresent()) {
+            throw new IllegalStateException("Both sourceInputStream and sourceFile have been set");
+        }
+        if (getSourceFile().isPresent() && !getSourceFile().filter(Files::isReadable).isPresent()) {
+            throw new IOException("Can't read file: " + sourceFile);
+        }
     }
-
     /**
-     * Gets the set source {@link Path}.
+     * Subclasses can override this method to check the target is valid.
      * <p>
-     * If this is {@link Optional#isPresent()}, then
-     * {@link #getSourceInputStream()} and {@link #getSourceInputStream()} are
-     * {@link Optional#empty()}.
-     *
-     * @return The source {@link IRI}, or {@link Optional#empty()} if it has not
-     *         been set
+     * The default implementation throws an IllegalStateException if the target
+     * has not been set.
      */
-    public Optional<IRI> getSourceIri() {
-        return Optional.ofNullable(sourceIri);
+    protected void checkTarget() {
+        if (target == null) {
+            throw new IllegalStateException("target has not been set");
+        }
+        if (getTargetGraph().isPresent() && getTargetDataset().isPresent()) {
+            // This should not happen as each target(..) method resets the
+            // optionals
+            throw new IllegalStateException("targetGraph and targetDataset can't both be set");
+        }
     }
 
-    private RDF rdfTermFactory;
-    private RDFSyntax contentTypeSyntax;
-    private String contentType;
-    private IRI base;
-    private InputStream sourceInputStream;
-    private Path sourceFile;
-    private IRI sourceIri;
-    private Consumer<Quad> target;
-    private Dataset targetDataset;
-    private Graph targetGraph;
-
     @SuppressWarnings("unchecked")
     @Override
     public T clone() {
@@ -227,22 +238,6 @@ public abstract class AbstractRDFParser<T extends AbstractRDFParser<T>> implemen
         }
     }
 
-    /**
-     * Returns this.
-     * @return this.
-     */
-    @SuppressWarnings("unchecked")
-    protected T asT() {
-        return (T) this;
-    }
-
-    @Override
-    public T rdfTermFactory(final RDF rdfTermFactory) {
-        final AbstractRDFParser<T> c = clone();
-        c.rdfTermFactory = rdfTermFactory;
-        return c.asT();
-    }
-
     @Override
     public T contentType(final RDFSyntax rdfSyntax) throws IllegalArgumentException {
         final AbstractRDFParser<T> c = clone();
@@ -259,137 +254,168 @@ public abstract class AbstractRDFParser<T extends AbstractRDFParser<T>> implemen
         return c.asT();
     }
 
-    @Override
-    public T base(final IRI base) {
-        final AbstractRDFParser<T> c = clone();
-        c.base = base;
-        c.getBase().ifPresent(this::checkIsAbsolute);
-        return c.asT();
+    /**
+     * Create a new {@link RDF} for a parse session.
+     * <p>
+     * This is called by {@link #parse()} to set {@link #rdfTermFactory(RDF)} if
+     * it is {@link Optional#empty()}.
+     * <p>
+     * As parsed blank nodes might be made with
+     * {@link RDF#createBlankNode(String)}, each call to this method SHOULD
+     * return a new RDF instance.
+     *
+     * @return A new {@link RDF}
+     */
+    protected RDF createRDFTermFactory() {
+        return new SimpleRDF();
     }
 
-    @Override
-    public T base(final String base) throws IllegalArgumentException {
-        return base(internalRdfTermFactory.createIRI(base));
+    /**
+     * Gets the set base {@link IRI}, if present.
+     *
+     * @return The base {@link IRI}, or {@link Optional#empty()} if it has not
+     *         been set
+     */
+    public Optional<IRI> getBase() {
+        return Optional.ofNullable(base);
     }
 
-    @Override
-    public T source(final InputStream inputStream) {
-        final AbstractRDFParser<T> c = clone();
-        c.resetSource();
-        c.sourceInputStream = inputStream;
-        return c.asT();
+    /**
+     * Gets the set content-type String, if any.
+     * <p>
+     * If this is {@link Optional#isPresent()} and is recognized by
+     * {@link RDFSyntax#byMediaType(String)}, then the corresponding
+     * {@link RDFSyntax} is set on {@link #getContentType()}, otherwise that is
+     * {@link Optional#empty()}.
+     *
+     * @return The Content-Type IANA media type, e.g. <code>text/turtle</code>,
+     *         or {@link Optional#empty()} if it has not been set
+     */
+    public final Optional<String> getContentType() {
+        return Optional.ofNullable(contentType);
     }
 
-    @Override
-    public T source(final Path file) {
-        final AbstractRDFParser<T> c = clone();
-        c.resetSource();
-        c.sourceFile = file;
-        return c.asT();
+    /**
+     * Gets the set content-type {@link RDFSyntax}, if any.
+     * <p>
+     * If this is {@link Optional#isPresent()}, then {@link #getContentType()}
+     * contains the value of {@link RDFSyntax#mediaType}.
+     *
+     * @return The {@link RDFSyntax} of the content type, or
+     *         {@link Optional#empty()} if it has not been set
+     */
+    public Optional<RDFSyntax> getContentTypeSyntax() {
+        return Optional.ofNullable(contentTypeSyntax);
     }
 
-    @Override
-    public T source(final IRI iri) {
-        final AbstractRDFParser<T> c = clone();
-        c.resetSource();
-        c.sourceIri = iri;
-        c.getSourceIri().ifPresent(this::checkIsAbsolute);
-        return c.asT();
+    /**
+     * Gets the set {@link RDF}, if any.
+     *
+     * @return The {@link RDF} to use, or {@link Optional#empty()} if it has not
+     *         been set
+     */
+    public Optional<RDF> getRdfTermFactory() {
+        return Optional.ofNullable(rdfTermFactory);
     }
 
-    @Override
-    public T source(final String iri) throws IllegalArgumentException {
-        final AbstractRDFParser<T> c = clone();
-        c.resetSource();
-        c.sourceIri = internalRdfTermFactory.createIRI(iri);
-        c.getSourceIri().ifPresent(this::checkIsAbsolute);
-        return source(internalRdfTermFactory.createIRI(iri));
+    /**
+     * Gets the set source {@link Path}.
+     * <p>
+     * If this is {@link Optional#isPresent()}, then
+     * {@link #getSourceInputStream()} and {@link #getSourceIri()} are
+     * {@link Optional#empty()}.
+     *
+     * @return The source {@link Path}, or {@link Optional#empty()} if it has
+     *         not been set
+     */
+    public Optional<Path> getSourceFile() {
+        return Optional.ofNullable(sourceFile);
     }
 
     /**
-     * Check if an iri is absolute.
+     * Gets the set source {@link InputStream}.
      * <p>
-     * Used by {@link #source(String)} and {@link #base(String)}.
+     * If this is {@link Optional#isPresent()}, then {@link #getSourceFile()}
+     * and {@link #getSourceIri()} are {@link Optional#empty()}.
      *
-     * @param iri
-     *            IRI to check
-     * @throws IllegalArgumentException
-     *             If the IRI is not absolute
+     * @return The source {@link InputStream}, or {@link Optional#empty()} if it
+     *         has not been set
      */
-    protected void checkIsAbsolute(final IRI iri) throws IllegalArgumentException {
-        if (!URI.create(iri.getIRIString()).isAbsolute()) {
-            throw new IllegalArgumentException("IRI is not absolute: " + iri);
-        }
+    public Optional<InputStream> getSourceInputStream() {
+        return Optional.ofNullable(sourceInputStream);
     }
 
     /**
-     * Check that one and only one source is present and valid.
-     * <p>
-     * Used by {@link #parse()}.
+     * Gets the set source {@link Path}.
      * <p>
-     * Subclasses might override this method, e.g. to support other source
-     * combinations, or to check if the sourceIri is resolvable.
+     * If this is {@link Optional#isPresent()}, then
+     * {@link #getSourceInputStream()} and {@link #getSourceInputStream()} are
+     * {@link Optional#empty()}.
      *
-     * @throws IOException
-     *             If a source file can't be read
+     * @return The source {@link IRI}, or {@link Optional#empty()} if it has not
+     *         been set
      */
-    protected void checkSource() throws IOException {
-        if (!getSourceFile().isPresent() && !getSourceInputStream().isPresent() && !getSourceIri().isPresent()) {
-            throw new IllegalStateException("No source has been set");
-        }
-        if (getSourceIri().isPresent() && getSourceInputStream().isPresent()) {
-            throw new IllegalStateException("Both sourceIri and sourceInputStream have been set");
-        }
-        if (getSourceIri().isPresent() && getSourceFile().isPresent()) {
-            throw new IllegalStateException("Both sourceIri and sourceFile have been set");
-        }
-        if (getSourceInputStream().isPresent() && getSourceFile().isPresent()) {
-            throw new IllegalStateException("Both sourceInputStream and sourceFile have been set");
-        }
-        if (getSourceFile().isPresent() && !getSourceFile().filter(Files::isReadable).isPresent()) {
-            throw new IOException("Can't read file: " + sourceFile);
-        }
+    public Optional<IRI> getSourceIri() {
+        return Optional.ofNullable(sourceIri);
     }
 
     /**
-     * Check if base is required.
+     * Gets the target to consume parsed Quads.
+     * <p>
+     * From the call to {@link #parseSynchronusly()}, this will be a
+     * non-<code>null</code> value (as a target is a required setting).
+     *
+     * @return The target consumer of {@link Quad}s, or <code>null</code> if it
+     *         has not yet been set.
      *
-     * @throws IllegalStateException
-     *             if base is required, but not set.
      */
-    protected void checkBaseRequired() throws IllegalStateException {
-        if (!getBase().isPresent() && getSourceInputStream().isPresent()
-                && !getContentTypeSyntax().filter(t -> t == RDFSyntax.NQUADS || t == RDFSyntax.NTRIPLES).isPresent()) {
-            throw new IllegalStateException("base iri required for inputstream source");
-        }
+    public Consumer<Quad> getTarget() {
+        return target;
     }
 
     /**
-     * Reset all source* fields to Optional.empty()
+     * Gets the target dataset as set by {@link #target(Dataset)}.
      * <p>
-     * Subclasses should override this and call <code>super.resetSource()</code>
-     * if they need to reset any additional source* fields.
+     * The return value is {@link Optional#isPresent()} if and only if
+     * {@link #target(Dataset)} has been set, meaning that the implementation
+     * may choose to append parsed quads to the {@link Dataset} directly instead
+     * of relying on the generated {@link #getTarget()} consumer.
+     * <p>
+     * If this value is present, then {@link #getTargetGraph()} MUST be
+     * {@link Optional#empty()}.
      *
+     * @return The target Dataset, or {@link Optional#empty()} if another kind
+     *         of target has been set.
      */
-    protected void resetSource() {
-        sourceInputStream = null;
-        sourceIri = null;
-        sourceFile = null;
+    public Optional<Dataset> getTargetDataset() {
+        return Optional.ofNullable(targetDataset);
     }
 
     /**
-     * Reset all optional target* fields to {@link Optional#empty()}.
+     * Gets the target graph as set by {@link #target(Graph)}.
      * <p>
-     * Note that the consumer set for {@link #getTarget()} is
-     * <strong>note</strong> reset.
+     * The return value is {@link Optional#isPresent()} if and only if
+     * {@link #target(Graph)} has been set, meaning that the implementation may
+     * choose to append parsed triples to the {@link Graph} directly instead of
+     * relying on the generated {@link #getTarget()} consumer.
      * <p>
-     * Subclasses should override this and call <code>super.resetTarget()</code>
-     * if they need to reset any additional target* fields.
+     * If this value is present, then {@link #getTargetDataset()} MUST be
+     * {@link Optional#empty()}.
      *
+     * @return The target Graph, or {@link Optional#empty()} if another kind of
+     *         target has been set.
      */
-    protected void resetTarget() {
-        targetDataset = null;
-        targetGraph = null;
+    public Optional<Graph> getTargetGraph() {
+        return Optional.ofNullable(targetGraph);
+    }
+
+    @Override
+    public Future<ParseResult> parse() throws IOException, IllegalStateException {
+        final AbstractRDFParser<T> c = prepareForParsing();
+        return threadpool.submit(() -> {
+            c.parseSynchronusly();
+            return null;
+        });
     }
 
     /**
@@ -449,99 +475,73 @@ public abstract class AbstractRDFParser<T extends AbstractRDFParser<T>> implemen
         return c.asT();
     }
 
-    /**
-     * Subclasses can override this method to check the target is valid.
-     * <p>
-     * The default implementation throws an IllegalStateException if the target
-     * has not been set.
-     */
-    protected void checkTarget() {
-        if (target == null) {
-            throw new IllegalStateException("target has not been set");
-        }
-        if (getTargetGraph().isPresent() && getTargetDataset().isPresent()) {
-            // This should not happen as each target(..) method resets the
-            // optionals
-            throw new IllegalStateException("targetGraph and targetDataset can't both be set");
-        }
-    }
-
-    /**
-     * Subclasses can override this method to check compatibility with the
-     * contentType setting.
-     *
-     * @throws IllegalStateException
-     *             if the {@link #getContentType()} or
-     *             {@link #getContentTypeSyntax()} is not compatible or invalid
-     */
-    protected void checkContentType() throws IllegalStateException {
+    @Override
+    public T rdfTermFactory(final RDF rdfTermFactory) {
+        final AbstractRDFParser<T> c = clone();
+        c.rdfTermFactory = rdfTermFactory;
+        return c.asT();
     }
 
     /**
-     * Guess RDFSyntax from a local file's extension.
+     * Reset all source* fields to Optional.empty()
      * <p>
-     * This method can be used by subclasses if {@link #getContentType()} is not
-     * present and {@link #getSourceFile()} is set.
+     * Subclasses should override this and call <code>super.resetSource()</code>
+     * if they need to reset any additional source* fields.
      *
-     * @param path
-     *            Path which extension should be checked
-     * @return The {@link RDFSyntax} which has a matching
-     *         {@link RDFSyntax#fileExtension}, otherwise
-     *         {@link Optional#empty()}.
      */
-    protected static Optional<RDFSyntax> guessRDFSyntax(final Path path) {
-        return fileExtension(path).flatMap(RDFSyntax::byFileExtension);
+    protected void resetSource() {
+        sourceInputStream = null;
+        sourceIri = null;
+        sourceFile = null;
     }
 
     /**
-     * Return the file extension of a Path - if any.
+     * Reset all optional target* fields to {@link Optional#empty()}.
      * <p>
-     * The returned file extension includes the leading <code>.</code>
+     * Note that the consumer set for {@link #getTarget()} is
+     * <strong>note</strong> reset.
      * <p>
-     * Note that this only returns the last extension, e.g. the file extension
-     * for <code>archive.tar.gz</code> would be <code>.gz</code>
+     * Subclasses should override this and call <code>super.resetTarget()</code>
+     * if they need to reset any additional target* fields.
      *
-     * @param path
-     *            Path which file name might contain an extension
-     * @return File extension (including the leading <code>.</code>, or
-     *         {@link Optional#empty()} if the path has no extension
      */
-    private static Optional<String> fileExtension(final Path path) {
-        final Path fileName = path.getFileName();
-        if (fileName == null) {
-            return Optional.empty();
-        }
-        final String fileNameStr = fileName.toString();
-        final int last = fileNameStr.lastIndexOf(".");
-        if (last > -1) {
-            return Optional.of(fileNameStr.substring(last));
-        }
-        return Optional.empty();
+    protected void resetTarget() {
+        targetDataset = null;
+        targetGraph = null;
     }
 
-    /**
-     * Create a new {@link RDF} for a parse session.
-     * <p>
-     * This is called by {@link #parse()} to set {@link #rdfTermFactory(RDF)} if
-     * it is {@link Optional#empty()}.
-     * <p>
-     * As parsed blank nodes might be made with
-     * {@link RDF#createBlankNode(String)}, each call to this method SHOULD
-     * return a new RDF instance.
-     *
-     * @return A new {@link RDF}
-     */
-    protected RDF createRDFTermFactory() {
-        return new SimpleRDF();
+    @Override
+    public T source(final InputStream inputStream) {
+        final AbstractRDFParser<T> c = clone();
+        c.resetSource();
+        c.sourceInputStream = inputStream;
+        return c.asT();
     }
 
     @Override
-    public Future<ParseResult> parse() throws IOException, IllegalStateException {
-        final AbstractRDFParser<T> c = prepareForParsing();
-        return threadpool.submit(() -> {
-            c.parseSynchronusly();
-            return null;
-        });
+    public T source(final IRI iri) {
+        final AbstractRDFParser<T> c = clone();
+        c.resetSource();
+        c.sourceIri = iri;
+        c.getSourceIri().ifPresent(this::checkIsAbsolute);
+        return c.asT();
+    }
+
+    @Override
+    public T source(final Path file) {
+        final AbstractRDFParser<T> c = clone();
+        c.resetSource();
+        c.sourceFile = file;
+        return c.asT();
+    }
+
+    @Override
+    public T source(final String iri) throws IllegalArgumentException {
+        final AbstractRDFParser<T> c = clone();
+        c.resetSource();
+        c.sourceIri = internalRdfTermFactory.createIRI(iri);
+        c.getSourceIri().ifPresent(this::checkIsAbsolute);
+        return source(internalRdfTermFactory.createIRI(iri));
     }
 
     @Override
diff --git a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/experimental/RDFParseException.java b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/experimental/RDFParseException.java
index 881717c7..1a8a5120 100644
--- a/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/experimental/RDFParseException.java
+++ b/commons-rdf-simple/src/main/java/org/apache/commons/rdf/simple/experimental/RDFParseException.java
@@ -39,10 +39,9 @@ public class RDFParseException extends Exception {
      * Constructs a new instance.
      * @param builder TODO
      * @param message TODO
-     * @param cause TODO
      */
-    public RDFParseException(final RDFParser builder, final String message, final Throwable cause) {
-        super(message, cause);
+    public RDFParseException(final RDFParser builder, final String message) {
+        super(message);
         this.builder = builder;
     }
 
@@ -50,9 +49,10 @@ public class RDFParseException extends Exception {
      * Constructs a new instance.
      * @param builder TODO
      * @param message TODO
+     * @param cause TODO
      */
-    public RDFParseException(final RDFParser builder, final String message) {
-        super(message);
+    public RDFParseException(final RDFParser builder, final String message, final Throwable cause) {
+        super(message, cause);
         this.builder = builder;
     }