commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ggreg...@apache.org
Subject [11/22] commons-rdf git commit: Module names, directory names, and artifact names should match.
Date Mon, 30 Oct 2017 14:57:11 GMT
http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/pom.xml
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/pom.xml b/commons-rdf-rdf4j/pom.xml
new file mode 100644
index 0000000..4448e74
--- /dev/null
+++ b/commons-rdf-rdf4j/pom.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.apache.commons</groupId>
+		<artifactId>commons-rdf-parent</artifactId>
+		<version>0.5.0-SNAPSHOT</version>
+	</parent>
+
+	<artifactId>commons-rdf-rdf4j</artifactId>
+	<packaging>jar</packaging>
+
+	<name>Commons RDF impl: RDF4j</name>
+	<description>Eclipse RDF4j implementation of Commons RDF API</description>
+
+	<build>
+		<plugins>
+			<plugin>
+
+				<groupId>org.apache.felix</groupId>
+				<artifactId>maven-bundle-plugin</artifactId>
+				<configuration>
+					<instructions>
+						<!-- Not sure why this is needed, .impl and .internal is normally hidden 
+							by the Bundle Plugin.. -->
+
+						<Export-Package>org.apache.commons.rdf.rdf4j</Export-Package>
+						<Private-Package>org.apache.commons.rdf.rdf4j.impl</Private-Package>
+						<Bundle-SymbolicName>org.apache.commons.rdf.rdf4j</Bundle-SymbolicName>
+						<Automatic-Module-Name>org.apache.commons.rdf.rdf4j</Automatic-Module-Name>
+						<Require-Capability>osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)";resolution:=optional</Require-Capability>
+						<Provide-Capability>osgi.serviceloader; osgi.serviceloader=org.apache.commons.rdf.api.RDF</Provide-Capability>
+					</instructions>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+
+    <distributionManagement>
+      <site>
+        <id>commonsrdf-api-site</id>
+        <url>scm:svn:${commons.scmPubUrl}/rdf4j/</url>
+      </site>
+    </distributionManagement>
+
+	<dependencies>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>commons-rdf-api</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>commons-rdf-simple</artifactId>
+			<version>${project.version}</version>
+		</dependency>
+
+		<!-- Selected rdf4j dependencies We don't use rdf4j-runtime or rdf4j-runtime-osgi 
+			here, as they pull in various depenencies like lucene, which might not be 
+			needed. -->
+
+		<dependency>
+			<groupId>org.eclipse.rdf4j</groupId>
+			<artifactId>rdf4j-model</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.rdf4j</groupId>
+			<artifactId>rdf4j-repository-api</artifactId>
+		</dependency>
+
+		<!-- For RDF4JParser, these are the RIO dependencies for the standard 
+			RDF 1.1 languages -->
+		<dependency>
+			<groupId>org.eclipse.rdf4j</groupId>
+			<artifactId>rdf4j-rio-turtle</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.rdf4j</groupId>
+			<artifactId>rdf4j-rio-ntriples</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.rdf4j</groupId>
+			<artifactId>rdf4j-rio-nquads</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.rdf4j</groupId>
+			<artifactId>rdf4j-rio-rdfxml</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.rdf4j</groupId>
+			<artifactId>rdf4j-rio-trig</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.rdf4j</groupId>
+			<artifactId>rdf4j-rio-jsonld</artifactId>
+		</dependency>
+		<!-- Languages notably missing, and why: 
+		
+		rdf4j-rio-rdfjson: standard, but https://www.w3.org/TR/rdf-json/ says:
+			"The syntax defined in this document should not be used unless there is a specific reason to do so" 
+			
+		rdf4j-rio-binary: non-standard 
+		
+		rdf4j-rio-n3: unofficial standard, super-seeded by Turtle
+			
+		rdf4j-rio-trix: unofficial standard 
+		-->
+
+
+		<!-- There are multiple RDF4J sail/repository implementations, we use the 
+			below from RDF4JFactory. -->
+		<dependency>
+			<groupId>org.eclipse.rdf4j</groupId>
+			<artifactId>rdf4j-sail-memory</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>org.eclipse.rdf4j</groupId>
+			<artifactId>rdf4j-repository-sail</artifactId>
+		</dependency>
+		<!--  and nativerdf for testing -->
+		<dependency>
+			<groupId>org.eclipse.rdf4j</groupId>
+			<artifactId>rdf4j-sail-nativerdf</artifactId>
+			<scope>test</scope>
+		</dependency>
+
+
+		<dependency>
+			<groupId>${project.parent.groupId}</groupId>
+			<artifactId>commons-rdf-api</artifactId>
+			<version>${project.version}</version>
+			<classifier>tests</classifier>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+
+
+	<!-- Ensure consistent rdf4j versions across the board -->
+	<dependencyManagement>
+		<dependencies>
+			<dependency>
+				<groupId>org.eclipse.rdf4j</groupId>
+				<artifactId>rdf4j-bom</artifactId>
+				<version>${rdf4j.version}</version>
+				<type>pom</type>
+				<scope>import</scope>
+			</dependency>
+		</dependencies>
+	</dependencyManagement>
+
+</project>

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/ClosableIterable.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/ClosableIterable.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/ClosableIterable.java
new file mode 100644
index 0000000..d888b0a
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/ClosableIterable.java
@@ -0,0 +1,38 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j;
+
+/**
+ * An {@link Iterable} which should be {@link #close()}d after use.
+ * <p>
+ * A good pattern to use this iterator is with an outer try-with-resources
+ * block: <code>
+ * for (ClosableIterable&lt;Triple&gt; triples : graph.iterate()) {
+ *     for (Triple t : triples) {
+ *       return t; // OK to terminate for-loop early
+ *     }
+ * }
+ * </code> The above will ensure that underlying resources are closed even if
+ * the iteration does not exhaust all triples.
+ *
+ * @param <T>
+ *            type of elements returned by the iterator
+ */
+public interface ClosableIterable<T> extends Iterable<T>, AutoCloseable {
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4J.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..44afa0a
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4J.java
@@ -0,0 +1,659 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j;
+
+import java.util.Arrays;
+import java.util.EnumSet;
+import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+
+// To avoid confusion, avoid importing
+// classes that are in both
+// commons.rdf and openrdf.model (e.g. IRI, Literal)
+import org.apache.commons.rdf.api.BlankNode;
+import org.apache.commons.rdf.api.BlankNodeOrIRI;
+import org.apache.commons.rdf.api.Dataset;
+import org.apache.commons.rdf.api.Graph;
+import org.apache.commons.rdf.api.Quad;
+import org.apache.commons.rdf.api.RDFTerm;
+import org.apache.commons.rdf.api.RDF;
+import org.apache.commons.rdf.api.Triple;
+import org.apache.commons.rdf.api.TripleLike;
+import org.apache.commons.rdf.rdf4j.impl.InternalRDF4JFactory;
+import org.eclipse.rdf4j.model.BNode;
+import org.eclipse.rdf4j.model.IRI;
+import org.eclipse.rdf4j.model.Literal;
+import org.eclipse.rdf4j.model.Model;
+import org.eclipse.rdf4j.model.Resource;
+import org.eclipse.rdf4j.model.Statement;
+import org.eclipse.rdf4j.model.Value;
+import org.eclipse.rdf4j.model.ValueFactory;
+import org.eclipse.rdf4j.model.impl.LinkedHashModel;
+import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
+import org.eclipse.rdf4j.repository.Repository;
+import org.eclipse.rdf4j.repository.RepositoryConnection;
+import org.eclipse.rdf4j.repository.sail.SailRepository;
+import org.eclipse.rdf4j.sail.Sail;
+import org.eclipse.rdf4j.sail.memory.MemoryStore;
+
+/**
+ * RDF4J implementation of RDF.
+ * <p>
+ * The {@link #RDF4J()} constructor uses a {@link SimpleValueFactory} to create
+ * corresponding RDF4J {@link Value} instances. Alternatively, this factory can
+ * be constructed with a different {@link ValueFactory} using
+ * {@link #RDF4J(ValueFactory)}.
+ * <p>
+ * {@link #asRDFTerm(Value)} can be used to convert any RDF4J {@link Value} to
+ * an RDFTerm. Note that adapted {@link BNode}s are considered equal if they are
+ * converted with the same {@link RDF4J} instance and have the same
+ * {@link BNode#getID()}.
+ * <p>
+ * {@link #createGraph()} creates a new Graph backed by {@link LinkedHashModel}.
+ * To use other models, see {@link #asGraph(Model)}.
+ * <p>
+ * To adapt a RDF4J {@link Repository} as a {@link Dataset} or {@link Graph},
+ * use {@link #asDataset(Repository, Option...)} or
+ * {@link #asGraph(Repository, Option...)}.
+ * <p>
+ * {@link #asTriple(Statement)} can be used to convert a RDF4J {@link Statement}
+ * to a Commons RDF {@link Triple}, and equivalent {@link #asQuad(Statement)} to
+ * convert a {@link Quad}.
+ * <p>
+ * To convert any {@link Triple} or {@link Quad} to to RDF4J {@link Statement},
+ * use {@link #asStatement(TripleLike)}. This recognises previously converted
+ * {@link RDF4JTriple}s and {@link RDF4JQuad}s without re-converting their
+ * {@link RDF4JTripleLike#asStatement()}.
+ * <p>
+ * Likewise, {@link #asValue(RDFTerm)} can be used to convert any Commons RDF
+ * {@link RDFTerm} to a corresponding RDF4J {@link Value}. This recognises
+ * previously converted {@link RDF4JTerm}s without re-converting their
+ * {@link RDF4JTerm#asValue()}.
+ * <p>
+ * For the purpose of {@link BlankNode} equivalence, this factory contains an
+ * internal {@link UUID} salt that is used by adapter methods like
+ * {@link #asQuad(Statement)}, {@link #asTriple(Statement)},
+ * {@link #asRDFTerm(Value)} as well as {@link #createBlankNode(String)}. As
+ * RDF4J {@link BNode} instances from multiple repositories or models may have
+ * the same {@link BNode#getID()}, converting them with the above methods might
+ * cause accidental {@link BlankNode} equivalence. Note that the {@link Graph}
+ * and {@link Dataset} adapter methods like
+ * {@link #asDataset(Repository, Option...)} and
+ * {@link #asGraph(Repository, Option...)} therefore uses a unique {@link RDF4J}
+ * internally.
+ * 
+ * @see RDF
+ *
+ */
+public final class RDF4J implements RDF {
+
+    /**
+     * InternalRDF4JFactory is deliberately abstract
+     */
+    private static InternalRDF4JFactory rdf4j = new InternalRDF4JFactory() {
+    };
+
+    public enum Option {
+        /**
+         * The Graph/Dataset should include any inferred statements
+         */
+        includeInferred,
+        /**
+         * The graph/dataset should handle {@link Repository#initialize()} (if
+         * needed) and {@link Repository#shutDown()} on {@link Graph#close()} /
+         * {@link Dataset#close()}.
+         */
+        handleInitAndShutdown
+    }
+
+    private final UUID salt;
+
+    private final ValueFactory valueFactory;
+
+    /**
+     * Construct an {@link RDF4J}.
+     * 
+     */
+    public RDF4J() {
+        this(SimpleValueFactory.getInstance(), UUID.randomUUID());
+    }
+
+    /**
+     * 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>
+     * This constructor may be used if reproducible
+     * {@link BlankNode#uniqueReference()} in {@link BlankNode} is desirable.
+     * 
+     * @param salt
+     *            An {@link UUID} salt to be used by any created
+     *            {@link BlankNode}s for the purpose of
+     *            {@link BlankNode#uniqueReference()}
+     */
+    public RDF4J(final UUID salt) {
+        this(SimpleValueFactory.getInstance(), salt);
+    }
+
+    /**
+     * Construct an {@link RDF4J}.
+     * <p>
+     * This constructor may be used if reproducible
+     * {@link BlankNode#uniqueReference()} in {@link BlankNode} is desirable.
+     * 
+     * @param valueFactory
+     *            The RDF4J {@link ValueFactory} to use
+     * @param salt
+     *            An {@link UUID} salt to be used by any created
+     *            {@link BlankNode}s for the purpose of
+     *            {@link BlankNode#uniqueReference()}
+     */
+    public RDF4J(final ValueFactory valueFactory, final UUID salt) {
+        this.valueFactory = valueFactory;
+        this.salt = salt;
+    }
+
+    /**
+     * 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 {@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 statement
+     *            The statement to convert
+     * @return A {@link RDF4JQuad} that is equivalent to the statement
+     */
+    public RDF4JQuad asQuad(final Statement statement) {
+        return rdf4j.createQuadImpl(statement, salt);
+    }
+
+    /**
+     *
+     * 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}
+     * <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.
+     * @return A {@link RDFTerm} that corresponds to the RDF4J value
+     * @throws IllegalArgumentException
+     *             if the value is not a BNode, Literal or IRI
+     */
+    public RDF4JTerm asRDFTerm(final Value value) {
+        return asRDFTerm(value, salt);
+    }
+
+    /**
+     *
+     * Adapt a RDF4J
+     * {@link org.eclipse.rdf4j.model.BNode} as a Commons RDF
+     * {@link org.apache.commons.rdf.api.BlankNode}
+     * <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 BNode} to convert.
+     * @return A {@link RDF4JBlankNode} that corresponds to the RDF4J BNode
+     */
+    public RDF4JBlankNode asRDFTerm(final BNode value) {
+        return rdf4j.createBlankNodeImpl(value, salt);
+    }
+
+    /**
+     *
+     * Adapt a RDF4J
+     * {@link org.eclipse.rdf4j.model.Literal} as a Commons RDF
+     * {@link org.apache.commons.rdf.api.Literal}
+     * <p>
+     * @param value
+     *            The RDF4J {@link Literal} to convert.
+     * @return A {@link RDF4JLiteral} that corresponds to the RDF4J literal
+     */
+    public RDF4JLiteral asRDFTerm(final Literal value) {
+        return rdf4j.createLiteralImpl(value);
+    }
+
+    /**
+     *
+     * Adapt a RDF4J
+     * {@link org.eclipse.rdf4j.model.IRI} as a Commons RDF
+     * {@link org.apache.commons.rdf.api.IRI}
+     * <p>
+     * @param value
+     *            The RDF4J {@link Value} to convert.
+     * @return A {@link RDF4JIRI} that corresponds to the RDF4J IRI
+     */
+    public RDF4JIRI asRDFTerm(final org.eclipse.rdf4j.model.IRI value) {
+        return rdf4j.createIRIImpl(value);
+    }
+
+    /**
+     *
+     * Adapt a RDF4J
+     * {@link org.eclipse.rdf4j.model.Resource} as a Commons RDF
+     * {@link org.apache.commons.rdf.api.BlankNodeOrIRI}
+     * <p>
+     * @param value
+     *            The RDF4J {@link Value} to convert.
+     * @return A {@link RDF4JBlankNodeOrIRI} that corresponds to the RDF4J Resource
+     */
+    public RDF4JBlankNodeOrIRI asRDFTerm(final org.eclipse.rdf4j.model.Resource value) {
+        if(value instanceof IRI){
+            return asRDFTerm((IRI)value);
+        } else if (value instanceof BNode){
+            return asRDFTerm((BNode)value);
+        }
+        throw new IllegalArgumentException("Value is not a BNode or IRI: " + value.getClass());
+    }
+
+    /**
+     * 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 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), new Resource[] {}); // 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);
+    }
+
+    /**
+     * Adapt a Commons RDF {@link Triple} or {@link Quad} as a RDF4J
+     * {@link Statement}.
+     * <p>
+     * If the <code>tripleLike</code> argument is an {@link RDF4JTriple} or a
+     * {@link RDF4JQuad}, then its {@link RDF4JTripleLike#asStatement()} is
+     * returned as-is. Note that this means that a {@link RDF4JTriple} would
+     * preserve its {@link Statement#getContext()}, and that any
+     * {@link BlankNode}s would be deemed equivalent in RDF4J if they have the
+     * same {@link BNode#getID()}.
+     *
+     * @param tripleLike
+     *            A {@link Triple} or {@link Quad} to adapt
+     * @return A corresponding {@link Statement}
+     */
+    public Statement asStatement(final TripleLike tripleLike) {
+        if (tripleLike instanceof RDF4JTripleLike) {
+            // Return original statement - this covers both RDF4JQuad and
+            // RDF4JTriple
+            return ((RDF4JTripleLike) tripleLike).asStatement();
+        }
+
+        final org.eclipse.rdf4j.model.Resource subject = (org.eclipse.rdf4j.model.Resource) asValue(tripleLike.getSubject());
+        final org.eclipse.rdf4j.model.IRI predicate = (org.eclipse.rdf4j.model.IRI) asValue(tripleLike.getPredicate());
+        final Value object = asValue(tripleLike.getObject());
+
+        org.eclipse.rdf4j.model.Resource context = null;
+        if (tripleLike instanceof Quad) {
+            final Quad quad = (Quad) tripleLike;
+            context = (org.eclipse.rdf4j.model.Resource) asValue(quad.getGraphName().orElse(null));
+        }
+
+        return getValueFactory().createStatement(subject, predicate, object, context);
+    }
+
+    /**
+     * Adapt a RDF4J {@link Statement} as a Commons RDF {@link Triple}.
+     * <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 statements 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 statement
+     *            The RDF4J {@link Statement} to adapt.
+     * @return A {@link RDF4JTriple} that is equivalent to the statement
+     */
+    public RDF4JTriple asTriple(final Statement statement) {
+        return rdf4j.createTripleImpl(statement, salt);
+    }
+
+    /**
+     * Adapt a Commons RDF {@link RDFTerm} as a RDF4J {@link Value}.
+     * <p>
+     * The value will be of the same kind as the term, e.g. a
+     * {@link org.apache.commons.rdf.api.BlankNode} is converted to a
+     * {@link org.eclipse.rdf4j.model.BNode}, a
+     * {@link org.apache.commons.rdf.api.IRI} is converted to a
+     * {@link org.eclipse.rdf4j.model.IRI} and a
+     * {@link org.apache.commons.rdf.api.Literal} is converted to a
+     * {@link org.eclipse.rdf4j.model.Literal}.
+     * <p>
+     * If the provided {@link RDFTerm} is <code>null</code>, then the returned
+     * value is <code>null</code>.
+     * <p>
+     * If the provided term is an instance of {@link RDF4JTerm}, then the
+     * {@link RDF4JTerm#asValue()} is returned without any conversion. Note that
+     * this could mean that a {@link Value} from a different kind of
+     * {@link ValueFactory} could be returned.
+     *
+     * @param term
+     *            RDFTerm to adapt to RDF4J Value
+     * @return Adapted RDF4J {@link Value}
+     */
+    public Value asValue(final RDFTerm term) {
+        if (term == null) {
+            return null;
+        }
+        if (term instanceof RDF4JTerm) {
+            // One of our own - avoid converting again.
+            // (This is crucial to avoid double-escaping in BlankNode)
+            return ((RDF4JTerm) term).asValue();
+        }
+        if (term instanceof org.apache.commons.rdf.api.IRI) {
+            final org.apache.commons.rdf.api.IRI iri = (org.apache.commons.rdf.api.IRI) term;
+            return getValueFactory().createIRI(iri.getIRIString());
+        }
+        if (term instanceof org.apache.commons.rdf.api.Literal) {
+            final org.apache.commons.rdf.api.Literal literal = (org.apache.commons.rdf.api.Literal) term;
+            final String label = literal.getLexicalForm();
+            if (literal.getLanguageTag().isPresent()) {
+                final String lang = literal.getLanguageTag().get();
+                return getValueFactory().createLiteral(label, lang);
+            }
+            final org.eclipse.rdf4j.model.IRI dataType = (org.eclipse.rdf4j.model.IRI) asValue(literal.getDatatype());
+            return getValueFactory().createLiteral(label, dataType);
+        }
+        if (term instanceof BlankNode) {
+            // This is where it gets tricky to support round trips!
+            final BlankNode blankNode = (BlankNode) term;
+            // FIXME: The uniqueReference might not be a valid BlankNode
+            // identifier..
+            // does it have to be in RDF4J?
+            return getValueFactory().createBNode(blankNode.uniqueReference());
+        }
+        throw new IllegalArgumentException("RDFTerm was not an IRI, Literal or BlankNode: " + term.getClass());
+    }
+
+    @Override
+    public RDF4JBlankNode createBlankNode() {
+        final BNode bnode = getValueFactory().createBNode();
+        return asRDFTerm(bnode);
+    }
+
+    @Override
+    public RDF4JBlankNode createBlankNode(final String name) {
+        final BNode bnode = getValueFactory().createBNode(name);
+        return asRDFTerm(bnode);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <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()}.
+     * 
+     */
+    @Override
+    public RDF4JDataset createDataset() {
+        final Sail sail = new MemoryStore();
+        final Repository repository = new SailRepository(sail);
+        return rdf4j.createRepositoryDatasetImpl(repository, true, false);
+    }
+
+    @Override
+    public RDF4JGraph createGraph() {
+        return asGraph(new LinkedHashModel());
+    }
+
+    @Override
+    public RDF4JIRI createIRI(final String iri) throws IllegalArgumentException {
+        return asRDFTerm(getValueFactory().createIRI(iri));
+    }
+
+    @Override
+    public RDF4JLiteral createLiteral(final String lexicalForm) throws IllegalArgumentException {
+        final org.eclipse.rdf4j.model.Literal lit = getValueFactory().createLiteral(lexicalForm);
+        return asRDFTerm(lit);
+    }
+
+    @Override
+    public org.apache.commons.rdf.api.Literal createLiteral(final String lexicalForm, final org.apache.commons.rdf.api.IRI dataType)
+            throws IllegalArgumentException {
+        final org.eclipse.rdf4j.model.IRI iri = getValueFactory().createIRI(dataType.getIRIString());
+        final org.eclipse.rdf4j.model.Literal lit = getValueFactory().createLiteral(lexicalForm, iri);
+        return asRDFTerm(lit);
+    }
+
+    @Override
+    public org.apache.commons.rdf.api.Literal createLiteral(final String lexicalForm, final String languageTag)
+            throws IllegalArgumentException {
+        final org.eclipse.rdf4j.model.Literal lit = getValueFactory().createLiteral(lexicalForm, languageTag);
+        return asRDFTerm(lit);
+    }
+
+    @Override
+    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));
+        return asTriple(statement);
+    }
+
+    @Override
+    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), (org.eclipse.rdf4j.model.Resource) asValue(graphName));
+        return asQuad(statement);
+    }
+
+    public ValueFactory getValueFactory() {
+        return valueFactory;
+    }
+
+    private EnumSet<Option> optionSet(final Option... options) {
+        final EnumSet<Option> opts = EnumSet.noneOf(Option.class);
+        opts.addAll(Arrays.asList(options));
+        return opts;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JBlankNode.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JBlankNode.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JBlankNode.java
new file mode 100644
index 0000000..14099e0
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JBlankNode.java
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j;
+
+import org.apache.commons.rdf.api.BlankNode;
+import org.eclipse.rdf4j.model.BNode;
+
+/**
+ * Marker interface for RDF4J implementations of Commons RDF
+ * {@link org.apache.commons.rdf.api.BlankNode}.
+ * <p>
+ * The underlying RDF4J {@link BNode} instance can be retrieved with
+ * {@link #asValue()}.
+ * 
+ * @see RDF4J#createBlankNode()
+ */
+public interface RDF4JBlankNode extends RDF4JBlankNodeOrIRI, BlankNode {
+
+    @Override
+    public BNode asValue();
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JBlankNodeOrIRI.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JBlankNodeOrIRI.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JBlankNodeOrIRI.java
new file mode 100644
index 0000000..dd7259e
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JBlankNodeOrIRI.java
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j;
+
+import org.apache.commons.rdf.api.BlankNodeOrIRI;
+import org.apache.commons.rdf.api.Triple;
+import org.eclipse.rdf4j.model.Resource;
+
+/**
+ * Marker interface for RDF4J implementations of Commons RDF
+ * {@link BlankNodeOrIRI} (e.g. the subject of a {@link Triple}).
+ * <p>
+ * The underlying RDF4J {@link org.eclipse.rdf4j.model.Resource} instance can be
+ * retrieved with {@link #asValue()}.
+ * 
+ */
+public interface RDF4JBlankNodeOrIRI extends RDF4JTerm, BlankNodeOrIRI {
+
+    @Override
+    public Resource asValue();
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JDataset.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..5740f44
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JDataset.java
@@ -0,0 +1,142 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j;
+
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.apache.commons.rdf.api.BlankNodeOrIRI;
+import org.apache.commons.rdf.api.Dataset;
+import org.apache.commons.rdf.api.IRI;
+import org.apache.commons.rdf.api.Quad;
+import org.apache.commons.rdf.api.RDFTerm;
+import org.apache.commons.rdf.rdf4j.RDF4J.Option;
+
+/**
+ * Marker interface for RDF4J implementations of Dataset.
+ * 
+ * @see RDF4J#createDataset()
+ * @see RDF4J#asDataset(org.eclipse.rdf4j.repository.Repository, Option...)
+ */
+public interface RDF4JDataset extends Dataset, RDF4JGraphLike<Quad> {
+
+    /**
+     * {@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()}.
+     * <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()
+     * }
+     * </pre>
+     */
+    @Override
+    Stream<RDF4JQuad> stream();
+
+    /**
+     * {@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()}.
+     * <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()
+     * }
+     * </pre>
+     */
+    @Override
+    Stream<RDF4JQuad> stream(Optional<BlankNodeOrIRI> graphName, BlankNodeOrIRI subject, IRI predicate, RDFTerm object);
+
+    /**
+     * {@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()}.
+     * <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()
+     * }
+     * </pre>
+     */
+    @Override
+    Stream<BlankNodeOrIRI> getGraphNames();
+
+    /**
+     * {@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()}.
+     * <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
+     *   }
+     * }
+     * </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();
+
+    /**
+     * {@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()}.
+     * <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
+     *   }
+     * }
+     * </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);
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JGraph.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..ab52310
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JGraph.java
@@ -0,0 +1,157 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j;
+
+import java.util.ConcurrentModificationException;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.apache.commons.rdf.api.BlankNodeOrIRI;
+import org.apache.commons.rdf.api.Graph;
+import org.apache.commons.rdf.api.IRI;
+import org.apache.commons.rdf.api.RDFTerm;
+import org.apache.commons.rdf.api.Triple;
+import org.eclipse.rdf4j.model.Model;
+import org.eclipse.rdf4j.repository.Repository;
+import org.apache.commons.rdf.rdf4j.RDF4J.Option;
+
+/**
+ * Marker interface for RDF4J implementations of Graph.
+ * 
+ * @see RDF4J#createGraph()
+ * @see RDF4J#asGraph(Model)
+ * @see RDF4J#asGraph(Repository, Option...)
+ * @see RDF4J#asGraphUnion(Repository, Option...)
+ * @see RDF4JDataset#getGraph()
+ * @see RDF4JDataset#getGraph(BlankNodeOrIRI)
+ */
+public interface RDF4JGraph extends Graph, RDF4JGraphLike<Triple> {
+
+    /**
+     * Return a copy of the context mask as a {@link Set} of
+     * {@link RDF4JBlankNodeOrIRI} graph names.
+     * <p>
+     * If the set is not {@link Set#isEmpty()}, the mask determines which
+     * <em>contexts</em> in the corresponding RDF4J {@link Model} or
+     * {@link Repository} that this graph reflect. Modifications to the graph
+     * (e.g. {@link #add(Triple)} will be performed for all the specified
+     * contexts, while retrieval (e.g. {@link #contains(Triple)}) will succeed
+     * if the triple is in at least one of the specified contexts.
+     * <p>
+     * The context mask array may contain <code>null</code>, indicating the
+     * default context (the <em>default graph</em> in RDF datasets).
+     * <p>
+     * If the context mask is {@link Set#isEmpty()}, then this is a <em>union
+     * graph</em> which triples reflect statements in any contexts. Triples
+     * added to the graph will be added in the default context, e.g. equivalent
+     * to <code>new Resource[1]{null}</code>) in RDF4J.
+     * <p>
+     * Note that the context mask itself cannot be <code>null</code>.
+     * <p>
+     * The returned set is an immutable copy; to specify a different mask, use
+     * {@link RDF4J#asGraph(Repository, Set, Option...)}
+     *
+     * @return The context mask as a set of {@link BlankNodeOrIRI} graph names,
+     *         which may contain the value <code>null</code>.
+     */
+    public Set<RDF4JBlankNodeOrIRI> getContextMask();
+
+    /**
+     * {@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()}.
+     * <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()
+     * }
+     * </pre>
+     */
+    @Override
+    Stream<RDF4JTriple> stream();
+
+    /**
+     * {@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()}.
+     * <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()
+     * }
+     * </pre>
+     */
+    @Override
+    Stream<RDF4JTriple> stream(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()}.
+     * <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
+     *   }
+     * }
+     * </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;
+
+    /**
+     * {@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()}.
+     * <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
+     *   }
+     * }
+     * </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);
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JGraphLike.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JGraphLike.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JGraphLike.java
new file mode 100644
index 0000000..4b69bee
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JGraphLike.java
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j;
+
+import java.util.Optional;
+
+import org.apache.commons.rdf.api.GraphLike;
+import org.apache.commons.rdf.api.TripleLike;
+import org.eclipse.rdf4j.model.Model;
+import org.eclipse.rdf4j.repository.Repository;
+
+/**
+ * Marker interface for RDF4J implementations of GraphLike.
+ * <p>
+ * This is a common interface for {@link RDF4JGraph} and {@link RDF4JDataset}
+ * which provides access to the underlying RDF4J {@link Model} and/or
+ * {@link Repository}.
+ * <p>
+ * At least one of {@link #asModel()} or {@link #asRepository()} will always be
+ * {@link Optional#isPresent()}.
+ * 
+ * @see RDF4JDataset
+ * @see RDF4JGraph
+ */
+public interface RDF4JGraphLike<T extends TripleLike> extends GraphLike<T>, AutoCloseable {
+
+    /**
+     * Return the corresponding RDF4J {@link Model}, if present.
+     * <p>
+     * The return value is {@link Optional#isPresent()} if this is backed by a
+     * Model.
+     * <p>
+     * Changes to the Model are reflected in both directions.
+     * 
+     * @return The corresponding RDF4J Model.
+     */
+    public Optional<Model> asModel();
+
+    /**
+     * Return the corresponding RDF4J {@link Repository}, if present.
+     * <p>
+     * The return value is {@link Optional#isPresent()} if this is backed by a
+     * Repository.
+     * <p>
+     * Changes to the Repository are reflected in both directions.
+     * 
+     * @return The corresponding RDF4J Repository.
+     */
+    public Optional<Repository> asRepository();
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JIRI.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JIRI.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JIRI.java
new file mode 100644
index 0000000..642ed58
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JIRI.java
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j;
+
+/**
+ * Marker interface for RDF4J implementations of Commons RDF
+ * {@link org.apache.commons.rdf.api.IRI}.
+ * <p>
+ * The underlying RDF4J {@link org.eclipse.rdf4j.model.IRI} instance can be
+ * retrieved with {@link #asValue()}.
+ * 
+ * @see RDF4J#createIRI(String)
+ */
+public interface RDF4JIRI extends RDF4JBlankNodeOrIRI, org.apache.commons.rdf.api.IRI {
+
+    @Override
+    org.eclipse.rdf4j.model.IRI asValue();
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JLiteral.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JLiteral.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JLiteral.java
new file mode 100644
index 0000000..6022da7
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JLiteral.java
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j;
+
+/**
+ * Marker interface for RDF4J implementations of Commons RDF
+ * {@link org.apache.commons.rdf.api.Literal}.
+ * <p>
+ * The underlying RDF4J {@link org.eclipse.rdf4j.model.Literal} instance can be
+ * retrieved with {@link #asValue()}.
+ * 
+ * @see RDF4J#createLiteral(String)
+ */
+public interface RDF4JLiteral extends RDF4JTerm, org.apache.commons.rdf.api.Literal {
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JQuad.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JQuad.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JQuad.java
new file mode 100644
index 0000000..7fedfde
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JQuad.java
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j;
+
+import org.apache.commons.rdf.api.Quad;
+
+/**
+ * Marker interface for RDF4J implementations of Quad.
+ * 
+ * @see RDF4J#createQuad(org.apache.commons.rdf.api.BlankNodeOrIRI,
+ *      org.apache.commons.rdf.api.BlankNodeOrIRI,
+ *      org.apache.commons.rdf.api.IRI, org.apache.commons.rdf.api.RDFTerm)
+ */
+public interface RDF4JQuad extends Quad, RDF4JTripleLike {
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JTerm.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JTerm.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JTerm.java
new file mode 100644
index 0000000..1f7b841
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JTerm.java
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j;
+
+import org.apache.commons.rdf.api.RDFTerm;
+import org.eclipse.rdf4j.model.BNode;
+import org.eclipse.rdf4j.model.IRI;
+import org.eclipse.rdf4j.model.Literal;
+import org.eclipse.rdf4j.model.Value;
+
+/**
+ * Marker interface for RDF4J implementations of RDFTerm.
+ * 
+ */
+public interface RDF4JTerm extends RDFTerm {
+
+    /**
+     * Return the RDF4J {@link Value} that this RDFTerm represents.
+     * 
+     * @return The wrapped {@link Value} (e.g. a {@link Literal}, {@link IRI} or
+     *         {@link BNode}.
+     */
+    public Value asValue();
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JTriple.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JTriple.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JTriple.java
new file mode 100644
index 0000000..55388ab
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JTriple.java
@@ -0,0 +1,30 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j;
+
+import org.apache.commons.rdf.api.Triple;
+
+/**
+ * Marker interface for RDF4J implementations of Triple.
+ * 
+ * @see RDF4J#createTriple(org.apache.commons.rdf.api.BlankNodeOrIRI,
+ *      org.apache.commons.rdf.api.IRI, org.apache.commons.rdf.api.RDFTerm)
+ */
+public interface RDF4JTriple extends Triple, RDF4JTripleLike {
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JTripleLike.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JTripleLike.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JTripleLike.java
new file mode 100644
index 0000000..222c2c8
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JTripleLike.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j;
+
+import org.apache.commons.rdf.api.TripleLike;
+import org.eclipse.rdf4j.model.Statement;
+
+/**
+ * Marker interface for RDF4J implementations of {@link TripleLike} statements.
+ * <p>
+ * This interface is in common with the more specific {@link RDF4JTriple} or
+ * {@link RDF4JQuad}.
+ * <p>
+ * This is backed by a {@link Statement} retrievable with
+ * {@link #asStatement()}.
+ * 
+ * @see RDF4JTriple
+ * @see RDF4JQuad
+ */
+public interface RDF4JTripleLike extends TripleLike {
+
+    /**
+     * Return the corresponding RDF4J {@link Statement}.
+     * 
+     * @return The corresponding RDF4J Statement.
+     */
+    public Statement asStatement();
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/experimental/RDF4JParser.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..53bcc00
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/experimental/RDF4JParser.java
@@ -0,0 +1,237 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j.experimental;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+import org.apache.commons.rdf.api.IRI;
+import org.apache.commons.rdf.api.Quad;
+import org.apache.commons.rdf.api.RDFSyntax;
+import org.apache.commons.rdf.rdf4j.RDF4J;
+import org.apache.commons.rdf.rdf4j.RDF4JBlankNodeOrIRI;
+import org.apache.commons.rdf.rdf4j.RDF4JDataset;
+import org.apache.commons.rdf.rdf4j.RDF4JGraph;
+import org.apache.commons.rdf.simple.experimental.AbstractRDFParser;
+import org.eclipse.rdf4j.model.Model;
+import org.eclipse.rdf4j.model.Resource;
+import org.eclipse.rdf4j.repository.util.RDFInserter;
+import org.eclipse.rdf4j.repository.util.RDFLoader;
+import org.eclipse.rdf4j.rio.ParserConfig;
+import org.eclipse.rdf4j.rio.RDFFormat;
+import org.eclipse.rdf4j.rio.RDFHandler;
+import org.eclipse.rdf4j.rio.RDFHandlerException;
+import org.eclipse.rdf4j.rio.Rio;
+import org.eclipse.rdf4j.rio.helpers.AbstractRDFHandler;
+
+/**
+ * RDF4J-based parser.
+ * <p>
+ * This can handle the RDF syntaxes {@link RDFSyntax#JSONLD},
+ * {@link RDFSyntax#NQUADS}, {@link RDFSyntax#NTRIPLES},
+ * {@link RDFSyntax#RDFXML}, {@link RDFSyntax#TRIG} and {@link RDFSyntax#TURTLE}
+ * - additional syntaxes can be supported by including the corresponding
+ * <em>rdf4j-rio-*</em> module on the classpath.
+ *
+ */
+public class RDF4JParser extends AbstractRDFParser<RDF4JParser> {
+
+    private final class AddToQuadConsumer extends AbstractRDFHandler {
+        private final Consumer<Quad> quadTarget;
+
+        private AddToQuadConsumer(final Consumer<Quad> quadTarget) {
+            this.quadTarget = quadTarget;
+        }
+
+        @Override
+        public void handleStatement(final org.eclipse.rdf4j.model.Statement st)
+                throws org.eclipse.rdf4j.rio.RDFHandlerException {
+            // TODO: if getRdfTermFactory() is a non-rdf4j factory, should
+            // we use factory.createQuad() instead?
+            // Unsure what is the promise of setting getRdfTermFactory() --
+            // does it go all the way down to creating BlankNode, IRI and
+            // Literal?
+            quadTarget.accept(rdf4jTermFactory.asQuad(st));
+            // Performance note:
+            // Graph/Quad.add should pick up again our
+            // RDF4JGraphLike.asStatement()
+            // and avoid double conversion.
+            // Additionally the RDF4JQuad and RDF4JTriple implementations
+            // are lazily converting subj/obj/pred/graph.s
+        }
+    }
+
+    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 org.eclipse.rdf4j.rio.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();
+
+    @Override
+    protected RDF4J createRDFTermFactory() {
+        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 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);
+    }
+
+    /**
+     * Get the RDF4J {@link ParserConfig} to use.
+     * <p>
+     * If no parser config is set, the default configuration is provided.
+     * <p>
+     * <strong>Note:</strong> The parser config is mutable - changes in the
+     * returned config is reflected in this instance of the parser. To avoid
+     * mutation, create a new {@link ParserConfig} and set
+     * {@link #setParserConfig(ParserConfig)}.
+     * 
+     * @return The RDF4J {@link ParserConfig}
+     */
+    public ParserConfig getParserConfig() {
+        return parserConfig;
+    }
+
+    /**
+     * Set 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
+        // using RDF4JGraphLike<TripleLike<BlankNodeOrIRI,IRI,RDFTerm>>
+        // or will that need tricky generics types?
+
+        if (getTargetDataset().filter(RDF4JDataset.class::isInstance).isPresent()) {
+            // One of us, we can add them as Statements directly
+            final RDF4JDataset dataset = (RDF4JDataset) getTargetDataset().get();
+            if (dataset.asRepository().isPresent()) {
+                return new RDFInserter(dataset.asRepository().get().getConnection());
+            }
+            if (dataset.asModel().isPresent()) {
+                final Model model = dataset.asModel().get();
+                return new AddToModel(model);
+            }
+            // Not backed by Repository or Model?
+            // Third-party RDF4JDataset subclass, so we'll fall through to the
+            // getTarget() handling further down
+        } else if (getTargetGraph().filter(RDF4JGraph.class::isInstance).isPresent()) {
+            final RDF4JGraph graph = (RDF4JGraph) getTargetGraph().get();
+
+            if (graph.asRepository().isPresent()) {
+                final RDFInserter inserter = new RDFInserter(graph.asRepository().get().getConnection());
+                if (!graph.getContextMask().isEmpty()) {
+                    final Stream<RDF4JBlankNodeOrIRI> b = graph.getContextMask().stream();
+                    final Stream<Resource> c = b.map(RDF4JBlankNodeOrIRI::asValue);
+                    final Resource[] contexts = c.toArray(Resource[]::new);
+                    inserter.enforceContext(contexts);
+                }
+                return inserter;
+            }
+            if (graph.asModel().isPresent() && graph.getContextMask().isEmpty()) {
+                // the model accepts any quad
+                final Model model = graph.asModel().get();
+                return new AddToModel(model);
+            }
+            // else - fall through
+        }
+
+        // Fall thorough: let target() consume our converted quads.
+        return new AddToQuadConsumer(getTarget());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/experimental/package-info.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/experimental/package-info.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/experimental/package-info.java
new file mode 100644
index 0000000..4f5b070
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/experimental/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ * Experimental Commons RDF RDF4J implementations.
+ * <p>
+ * Classes in this package should be considered <strong>at risk</strong>; they
+ * might change or be removed in the next minor update of Commons RDF.
+ * <p>
+ * When a class has stabilized, it will move to the
+ * {@link org.apache.commons.rdf.rdf4j} package.
+ * <ul>
+ * <li>{@link RDF4JParser} - an RDF4J-backed implementations of
+ * {@link org.apache.commons.rdf.experimental.RDFParser}.</li>
+ * </ul>
+ */
+package org.apache.commons.rdf.rdf4j.experimental;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/AbstractRDFTerm.java
----------------------------------------------------------------------
diff --git a/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/AbstractRDFTerm.java b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/AbstractRDFTerm.java
new file mode 100644
index 0000000..ea847b0
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/AbstractRDFTerm.java
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j.impl;
+
+import org.apache.commons.rdf.rdf4j.RDF4JTerm;
+import org.eclipse.rdf4j.model.Value;
+
+abstract class AbstractRDFTerm<T extends Value> implements RDF4JTerm {
+    T value;
+
+    AbstractRDFTerm(final T value) {
+        this.value = value;
+    }
+
+    @Override
+    public T asValue() {
+        return value;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/AbstractRepositoryGraphLike.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..21eef04
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/AbstractRepositoryGraphLike.java
@@ -0,0 +1,76 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j.impl;
+
+import java.util.Optional;
+import java.util.UUID;
+
+import org.apache.commons.rdf.api.TripleLike;
+import org.apache.commons.rdf.rdf4j.RDF4JGraphLike;
+import org.apache.commons.rdf.rdf4j.RDF4J;
+import org.eclipse.rdf4j.model.Model;
+import org.eclipse.rdf4j.model.Statement;
+import org.eclipse.rdf4j.repository.Repository;
+import org.eclipse.rdf4j.repository.RepositoryConnection;
+
+abstract class AbstractRepositoryGraphLike<T extends TripleLike> implements RDF4JGraphLike<T> {
+
+    protected final Repository repository;
+    protected final boolean includeInferred;
+    protected final boolean handleInitAndShutdown;
+    protected final RDF4J rdf4jTermFactory;
+    protected final UUID salt;
+
+    AbstractRepositoryGraphLike(final Repository repository, final UUID salt, final boolean handleInitAndShutdown,
+            final boolean includeInferred) {
+        this.repository = repository;
+        this.salt = salt;
+        this.includeInferred = includeInferred;
+        this.handleInitAndShutdown = handleInitAndShutdown;
+        if (handleInitAndShutdown && !repository.isInitialized()) {
+            repository.initialize();
+        }
+        rdf4jTermFactory = new RDF4J(repository.getValueFactory(), salt);
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (handleInitAndShutdown) {
+            repository.shutDown();
+        }
+        // else: repository was initialized outside, so we should not shut it
+        // 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();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/commons-rdf/blob/d59203ce/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/BlankNodeImpl.java
----------------------------------------------------------------------
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
new file mode 100644
index 0000000..7120273
--- /dev/null
+++ b/commons-rdf-rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/impl/BlankNodeImpl.java
@@ -0,0 +1,98 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.rdf.rdf4j.impl;
+
+import java.nio.charset.StandardCharsets;
+import java.util.UUID;
+
+import org.apache.commons.rdf.api.BlankNode;
+import org.apache.commons.rdf.rdf4j.RDF4JBlankNode;
+import org.eclipse.rdf4j.model.BNode;
+import org.eclipse.rdf4j.rio.turtle.TurtleUtil;
+
+final class BlankNodeImpl extends AbstractRDFTerm<BNode> implements RDF4JBlankNode {
+
+    private transient int hashCode = 0;
+    private final long saltUUIDleast;
+    private final long saltUUIDmost;
+
+    BlankNodeImpl(final BNode bNode, final UUID salt) {
+        super(bNode);
+        // Space-efficient storage of salt UUID
+        saltUUIDmost = salt.getMostSignificantBits();
+        saltUUIDleast = salt.getLeastSignificantBits();
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        // NOTE: Do NOT use Bnode.equals() as it has a more generous
+        // equality based only on the value.getID();
+        if (obj instanceof BlankNode) {
+            final BlankNode blankNode = (BlankNode) obj;
+            return uniqueReference().equals(blankNode.uniqueReference());
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        if (hashCode != 0) {
+            return hashCode;
+        }
+        return hashCode = uniqueReference().hashCode();
+    }
+
+    private boolean isValidBlankNodeLabel(final String id) {
+        // FIXME: Replace with a regular expression?
+        if (id.isEmpty()) {
+            return false;
+        }
+        if (!TurtleUtil.isBLANK_NODE_LABEL_StartChar(id.codePointAt(0))) {
+            return false;
+        }
+        for (int i = 1; i < id.length(); i++) {
+            if (!TurtleUtil.isBLANK_NODE_LABEL_Char(id.codePointAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public String ntriplesString() {
+        if (isValidBlankNodeLabel(value.getID())) {
+            return "_:" + value.getID();
+        } else {
+            return "_:" + UUID.nameUUIDFromBytes(value.getID().getBytes(StandardCharsets.UTF_8));
+        }
+    }
+
+    @Override
+    public String uniqueReference() {
+        final UUID uuid = new UUID(saltUUIDmost, saltUUIDleast);
+        return "urn:uuid:" + uuid + "#" + value.getID();
+    }
+    
+    @Override
+    public String toString() {
+        return ntriplesString() + " [" + uniqueReference() + "]";
+    }
+}
\ No newline at end of file


Mime
View raw message