Github user stain commented on a diff in the pull request:
https://github.com/apache/incubator-commonsrdf/pull/24#discussion_r81331455
--- 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);
--- End diff --
Yes, I think it would make more sense to hide the salt more and remove
those static methods, perhaps have just a `RDF4JTermFactory(UUID)` constructor.
Then the factory is not so locked into having to use UUID for salt.
_The reason to support a fixed `UUID` overall is if the user deliberately
want them to be compatible - e.g. in a distributed architecture where two cloud
instances are constructing named bnodes to be added to the same remote SAIL, or
if you want reproducible output. (similar to fixed random seeds in
simulations)._
I'll change it to just a constructor - removing the static methods will
simplify the factory.
In the
[JenaRDFTermFactory](http://stain.github.io/incubator-commonsrdf/integration/org/apache/commons/rdf/jena/JenaRDFTermFactory.html)
there's a different approach which I'm not sure what your views are on - the
static methods there allow any `RDFTermFactory` to be passed in (it would use
the `createBlankNode(String)` directly)
---
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.
---