Github user ansell commented on a diff in the pull request:
https://github.com/apache/incubator-commonsrdf/pull/24#discussion_r81252832
--- Diff:
rdf4j/src/main/java/org/apache/commons/rdf/rdf4j/RDF4JTermFactory.java ---
@@ -0,0 +1,509 @@
+/**
+ * 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.UUID;
+
+// To avoid confusion, avoid importing
+// classes that are in both
+// commons.rdf and openrdf.model (e.g. IRI)
+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.IRI;
+import org.apache.commons.rdf.api.Quad;
+import org.apache.commons.rdf.api.RDFTerm;
+import org.apache.commons.rdf.api.RDFTermFactory;
+import org.apache.commons.rdf.api.Triple;
+import org.apache.commons.rdf.api.TripleLike;
+import org.apache.commons.rdf.rdf4j.impl.BlankNodeImpl;
+import org.apache.commons.rdf.rdf4j.impl.IRIImpl;
+import org.apache.commons.rdf.rdf4j.impl.LiteralImpl;
+import org.apache.commons.rdf.rdf4j.impl.ModelGraphImpl;
+import org.apache.commons.rdf.rdf4j.impl.QuadImpl;
+import org.apache.commons.rdf.rdf4j.impl.RepositoryDatasetImpl;
+import org.apache.commons.rdf.rdf4j.impl.RepositoryGraphImpl;
+import org.apache.commons.rdf.rdf4j.impl.TripleImpl;
+import org.eclipse.rdf4j.model.BNode;
+import org.eclipse.rdf4j.model.Model;
+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;
+
+/**
+ * RDF4J implementation of RDFTermFactory
+ * <p>
+ * The {@link #RDF4JTermFactory()} 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 #RDF4JTermFactory(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 RDF4JTermFactory} 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 #asRDFTermGraph(Model)}.
+ * <p>
+ * To adapt a RDF4J {@link Repository} as a {@link Dataset} or {@link
Graph},
+ * use {@link #asRDFTermDataset(Repository)} or
+ * {@link #asRDFTermGraph(Repository)}.
+ * <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 #asRDFTermDataset(Repository)} and {@link #asRDFTermGraph(Model)}
+ * therefore uses a unique {@link RDF4JTermFactory} internally. An
alternative
+ * is to use the static methods {@link #asRDFTerm(Value, UUID)},
+ * {@link #asQuad(Statement, UUID)} or {@link #asTriple(Statement, UUID)}
with
+ * a provided {@link UUID} salt.
+ *
+ */
+public class RDF4JTermFactory implements RDFTermFactory {
+
+ /**
+ * 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.
+ * @param <T>
+ * The subclass of {@link Value}, e.g. {@link BNode}
+ * @return A {@link RDFTerm} that corresponds to the RDF4J value
+ * @throws IllegalArgumentException
+ * if the value is not a BNode, Literal or IRI
+ */
+ @SuppressWarnings("unchecked")
+ public static <T extends Value> RDF4JTerm<T> asRDFTerm(final T value,
UUID salt) {
+ if (value instanceof BNode) {
+ return (RDF4JTerm<T>) new BlankNodeImpl((BNode) value,
salt);
+ }
+ if (value instanceof org.eclipse.rdf4j.model.Literal) {
+ return (RDF4JTerm<T>) new
LiteralImpl((org.eclipse.rdf4j.model.Literal) value);
+ }
+ if (value instanceof org.eclipse.rdf4j.model.IRI) {
+ return (RDF4JTerm<T>) new
IRIImpl((org.eclipse.rdf4j.model.IRI) value);
+ }
+ throw new IllegalArgumentException("Value is not a BNode,
Literal or IRI: " + value.getClass());
+ }
+
+ /**
+ * Adapt a RDF4J {@link Statement} as a Commons RDF {@link Triple}.
+ *
+ * @param statement
+ * The statement 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 Triple} that corresponds to the RDF4J statement
+ */
+ public static RDF4JTriple asTriple(final Statement statement, UUID
salt) {
+ return new TripleImpl(statement, salt);
+ }
+
+ private UUID salt = UUID.randomUUID();
+
+ private final ValueFactory valueFactory;
+
+ public RDF4JTermFactory() {
+ this.valueFactory = SimpleValueFactory.getInstance();
+ }
+
+ public RDF4JTermFactory(ValueFactory valueFactory) {
+ this.valueFactory = valueFactory;
+ }
+
+ /**
+ * 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 RDF4JTermFactory}.
+ * <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 RDF4JTermFactory}
+ * instance is used per RDF4J repository/model.
+ *
+ * @see #asQuad(Statement, UUID)
+ * @param statement
+ * The statement to convert
+ * @return A {@link RDF4JQuad} that is equivalent to the statement
+ */
+ public RDF4JQuad asQuad(final Statement statement) {
+ return new QuadImpl(statement, salt);
+ }
+
+ /**
+ * Adapt a RDF4J {@link Statement} as a Commons RDF {@link Quad}.
+ *
+ * @see #asQuad(Statement)
+ * @param statement
+ * The statement 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 RDF4JQuad} that is equivalent to the statement
+ */
+ public static RDF4JQuad asQuad(final Statement statement, UUID salt) {
+ return new QuadImpl(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 RDF4JTermFactory}.
+ * <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 RDF4JTermFactory}
+ * instance is used per RDF4J repository/model.
+ *
+ * @param value The RDF4J {@link Value} to convert.
+ * @param <T>
+ * The subclass of {@link Value}, e.g. {@link BNode}
+ * @return A {@link RDFTerm} that corresponds to the RDF4J value
+ * @throws IllegalArgumentException if the value is not a BNode,
Literal or IRI
+ */
+ public <T extends Value> RDF4JTerm<T> asRDFTerm(T value) {
+ return asRDFTerm(value, salt);
+ }
+
+ /**
+ * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Dataset}.
+ * <p>
+ * Changes to the dataset are reflected in the repository, and vice
versa.
+ *
+ * @param repository
+ * RDF4J {@link Repository} to connect to.
+ * @return A {@link Dataset} backed by the RDF4J repository.
+ */
+ public RDF4JDataset asRDFTermDataset(Repository repository) {
+ return new RepositoryDatasetImpl(repository);
+ }
+
+ /**
+ * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Dataset}.
+ * <p>
+ * Changes to the dataset are reflected in the repository, and vice
versa.
+ *
+ * @param repository
+ * RDF4J {@link Repository} to connect to.
+ * @param includeInferred
+ * If true, any inferred quads are included in the dataset
+ * @return A {@link Dataset} backed by the RDF4J repository.
+ */
+ public RDF4JDataset asRDFTermDataset(Repository repository, boolean
includeInferred) {
+ return new RepositoryDatasetImpl(repository, 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 asRDFTermGraph(Model model) {
+ return new ModelGraphImpl(model);
+ }
+
+ /**
+ * 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.
+ * @return A {@link Graph} backed by the RDF4J repository.
+ */
+ public RDF4JGraph asRDFTermGraph(Repository repository) {
+ return new RepositoryGraphImpl(repository, false, true);
+ }
+
+ /**
+ * 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 includeInferred
+ * If true, any inferred triples are included in the graph
+ * @param unionGraph
+ * If true, triples from any context is included in the
graph,
+ * otherwise only triples in the default context
+ * <code>null</code>.
+ * @return A {@link Graph} backed by the RDF4J repository.
+ */
+ public RDF4JGraph asRDFTermGraph(Repository repository, boolean
includeInferred, boolean unionGraph) {
+ return new RepositoryGraphImpl(repository, includeInferred,
unionGraph);
+ }
+
+ /**
+ * 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(TripleLike<BlankNodeOrIRI,
org.apache.commons.rdf.api.IRI, RDFTerm> tripleLike) {
+ if (tripleLike instanceof RDF4JTripleLike) {
+ // Return original statement - this covers both
RDF4JQuad and
+ // RDF4JTriple
+ RDF4JTripleLike rdf4jTriple = (RDF4JTripleLike)
tripleLike;
--- End diff --
This would look a little simpler if you did the cast inline below instead
of creating a temporary variable here. The compiler or JVM will likely optimise
the variable away so no actual difference in practice, just not usual to cast
to a variable for one operation.
---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at [email protected] or file a JIRA ticket
with INFRA.
---