blob: a7511c709dbdef5bbbc42a7f4c8caa61edaedc0d [file] [log] [blame]
/**
* 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.jena;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Consumer;
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.Literal;
import org.apache.commons.rdf.api.Quad;
import org.apache.commons.rdf.api.QuadLike;
import org.apache.commons.rdf.api.RDFSyntax;
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.jena.impl.InternalJenaFactory;
import org.apache.jena.datatypes.RDFDatatype;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.query.DatasetFactory;
import org.apache.jena.riot.Lang;
import org.apache.jena.riot.RDFDataMgr;
import org.apache.jena.riot.RDFLanguages;
import org.apache.jena.riot.system.StreamRDF;
import org.apache.jena.riot.system.StreamRDFBase;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.DatasetGraphFactory;
import org.apache.jena.sparql.graph.GraphFactory;
/**
* Apache Jena RDF implementation.
* <p>
* Instances of JenaRDF can also convert existing objects from Jena with methods
* like {@link #asRDFTerm(Node)} and
* {@link #asGraph(org.apache.jena.graph.Graph)}, and vice versa from any
* Commons RDF object to Jena with the <code>asJena*</code> methods like
* {@link #asJenaNode(RDFTerm)} and {@link #asJenaGraph(Graph)}.
* <p>
* Note that Commons RDF objects created by this class implement the
* specializations interfaces like {@link JenaRDFTerm}, {@link JenaGraph} and
* {@link JenaTriple}, which provide access to the underlying Jena objects, e.g.
* with {@link JenaRDFTerm#asJenaNode()}.
* <p>
* For the purpose of {@link BlankNode} identity when using
* {@link #createBlankNode(String)} (see {@link BlankNode#equals(Object)} and
* {@link BlankNode#uniqueReference()}), each instance of JenaRDF uses an
* internal random state. If for some reason consistent/reproducible BlankNode
* identity is desired, it is possible to retrieve the state as a UUID using
* {@link #salt()} for subsequent use with {@link JenaRDF#JenaRDF(UUID)} - note
* that such consistency is only guaranteed within the same minor version of
* Commons RDF.
*
* @see RDF
*/
public final class JenaRDF implements RDF {
private static final InternalJenaFactory INTERNAL_JENA_FACTORY = new InternalJenaFactory() {
};
private final UUID salt;
/**
* Create a JenaRDF.
* <p>
* This constructor will use a randomly generated {@link UUID} as a salt for
* the purposes of {@link BlankNode} identity, see {@link #salt()}.
*/
public JenaRDF() {
this.salt = UUID.randomUUID();
}
/**
* Create a JenaRDF.
* <p>
* This constructor will use the specified {@link UUID} as a salt for the
* purposes of {@link BlankNode} identity, and should only be used in cases
* where predictable and consistent {@link BlankNode#uniqueReference()} are
* important.
*
* @param salt
* {@link UUID} to use as salt for {@link BlankNode} equality
*/
public JenaRDF(final UUID salt) {
this.salt = salt;
}
@Override
public JenaBlankNode createBlankNode() {
return INTERNAL_JENA_FACTORY.createBlankNode(salt());
}
@Override
public JenaBlankNode createBlankNode(final String name) {
return INTERNAL_JENA_FACTORY.createBlankNode(name, salt());
}
@Override
public JenaDataset createDataset() {
return INTERNAL_JENA_FACTORY.createDataset(salt());
}
@Override
public JenaGraph createGraph() {
return INTERNAL_JENA_FACTORY.createGraph(salt());
}
@Override
public JenaIRI createIRI(final String iri) {
validateIRI(iri);
return INTERNAL_JENA_FACTORY.createIRI(iri);
}
@Override
public JenaLiteral createLiteral(final String lexicalForm) {
return INTERNAL_JENA_FACTORY.createLiteral(lexicalForm);
}
@Override
public JenaLiteral createLiteral(final String lexicalForm, final IRI dataType) {
return INTERNAL_JENA_FACTORY.createLiteralDT(lexicalForm, dataType.getIRIString());
}
@Override
public JenaLiteral createLiteral(final String lexicalForm, final String languageTag) {
validateLang(languageTag);
return INTERNAL_JENA_FACTORY.createLiteralLang(lexicalForm, languageTag);
}
@Override
public JenaTriple createTriple(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
return INTERNAL_JENA_FACTORY.createTriple(subject, predicate, object);
}
/**
* {@inheritDoc}
* <p>
* In addition to supporting a <code>graphName</code> of <code>null</code>
* for representing a triple in the <em>default graph</em>, this method also
* recognize a {@link JenaIRI} which {@link JenaRDFTerm#asJenaNode()}
* represent the default graph according to
* {@link org.apache.jena.sparql.core.Quad#isDefaultGraph(Node)}, in which
* case the returned JenaQuad will have a {@link Quad#getGraphName()} of
* {@link Optional#empty()} rather than the provided IRI.
*
*/
@Override
public JenaQuad createQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object)
throws IllegalArgumentException, UnsupportedOperationException {
return INTERNAL_JENA_FACTORY.createQuad(subject, predicate, object, graphName);
}
/**
* Create a generalized Jena triple.
* <p>
* The <em>generalized triple</em> supports any {@link RDFTerm} as its
* {@link TripleLike#getSubject()} {@link TripleLike#getPredicate()} or
* {@link TripleLike#getObject()}.
*
* @see #createTriple(BlankNodeOrIRI, IRI, RDFTerm)
* @see #createGeneralizedQuad(RDFTerm, RDFTerm, RDFTerm, RDFTerm)
*
* @param subject
* The subject of the statement
* @param predicate
* The predicate of the statement
* @param object
* The object of the statement
* @return Generalized {@link TripleLike}. Note that the generalized triple
* does <strong>not</strong> implement {@link Triple#equals(Object)}
* or {@link Triple#hashCode()}.
*/
public JenaGeneralizedTripleLike createGeneralizedTriple(final RDFTerm subject, final RDFTerm predicate, final RDFTerm object) {
return INTERNAL_JENA_FACTORY.createGeneralizedTriple(subject, predicate, object);
}
/**
* Create a generalized Jena quad.
* <p>
* The <em>generalized quad</em> supports any {@link RDFTerm} as its
* {@link QuadLike#getSubject()} {@link QuadLike#getPredicate()},
* {@link QuadLike#getObject()} or {@link QuadLike#getObject()}.
* <p>
* In addition to supporting a <code>graphName</code> of <code>null</code>
* for representing a triple in the <em>default graph</em>, this method also
* recognize a {@link JenaIRI} which {@link JenaRDFTerm#asJenaNode()}
* represent the default graph according to
* {@link org.apache.jena.sparql.core.Quad#isDefaultGraph(Node)}, in which
* case the returned JenaQuad will have a {@link Quad#getGraphName()} of
* {@link Optional#empty()} rather than the provided IRI.
*
* @see #createQuad(BlankNodeOrIRI, BlankNodeOrIRI, IRI, RDFTerm)
* @see #createGeneralizedTriple(RDFTerm, RDFTerm, RDFTerm)
*
* @param subject
* The subject of the statement
* @param predicate
* The predicate of the statement
* @param object
* The object of the statement
* @param graphName
* The graph name of the statement
* @return Generalized {@link QuadLike}. Note that the generalized quad does
* <strong>not</strong> implement {@link Quad#equals(Object)} or
* {@link Quad#hashCode()}.
*/
public JenaGeneralizedQuadLike createGeneralizedQuad(final RDFTerm subject, final RDFTerm predicate, final RDFTerm object,
final RDFTerm graphName) {
return INTERNAL_JENA_FACTORY.createGeneralizedQuad(subject, predicate, object, graphName);
}
/**
* Adapt an existing Jena Node to CommonsRDF {@link RDFTerm}.
* <p>
* If {@link Node#isLiteral()}, then the returned value is a
* {@link Literal}. If {@link Node#isURI()}, the returned value is a IRI. If
* {$@link Node#isBlank()}, the returned value is a {@link BlankNode}, which
* will use a {@link UUID} salt from this {@link JenaRDF} instance in
* combination with {@link Node#getBlankNodeId()} for the purpose of its
* {@link BlankNode#uniqueReference()}.
*
* @see #asRDFTerm(RDF, Node)
*
* @param node
* The Jena Node to adapt. It's {@link Node#isConcrete()} must be
* <code>true</code>.
* @return Adapted {@link JenaRDFTerm}
* @throws ConversionException
* If the {@link Node} can't be represented as an
* {@link RDFTerm}, e.g. if the node is not concrete or
* represents a variable in Jena.
*/
public JenaRDFTerm asRDFTerm(final Node node) throws ConversionException {
return INTERNAL_JENA_FACTORY.createRDFTerm(node, salt());
}
/**
* Convert from Jena {@link Node} to any Commons RDF implementation.
* <p>
* Note that if the {@link Node#isBlank()}, then the factory's
* {@link RDF#createBlankNode(String)} will be used, meaning that care
* should be taken if reusing an {@link RDF} instance for multiple
* conversion sessions.
*
* @see #asRDFTerm(Node)
*
* @param factory
* {@link RDF} to use for creating {@link RDFTerm}.
* @param node
* The Jena Node to adapt. It's {@link Node#isConcrete()} must be
* <code>true</code>.
* @return Adapted {@link RDFTerm}
* @throws ConversionException
* If the {@link Node} can't be represented as an
* {@link RDFTerm}, e.g. if the node is not concrete or
* represents a variable in Jena.
*/
public static RDFTerm asRDFTerm(final RDF factory, final Node node) {
if (node == null) {
return null;
}
if (factory instanceof JenaRDF) {
// No need to convert, just wrap
return ((JenaRDF) factory).asRDFTerm(node);
}
if (node.isURI()) {
return factory.createIRI(node.getURI());
}
if (node.isLiteral()) {
final String lang = node.getLiteralLanguage();
if (lang != null && !lang.isEmpty()) {
return factory.createLiteral(node.getLiteralLexicalForm(), lang);
}
if (node.getLiteralDatatype().equals(XSDDatatype.XSDstring)) {
return factory.createLiteral(node.getLiteralLexicalForm());
}
final IRI dt = factory.createIRI(node.getLiteralDatatype().getURI());
return factory.createLiteral(node.getLiteralLexicalForm(), dt);
}
if (node.isBlank()) {
// The factory
return factory.createBlankNode(node.getBlankNodeLabel());
}
throw new ConversionException("Node is not a concrete RDF Term: " + node);
}
/**
* Adapt an existing Jena Triple to CommonsRDF {@link Triple}.
* <p>
* If the triple contains any {@link Node#isBlank()}, then any corresponding
* {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF}
* instance in combination with {@link Node#getBlankNodeId()} for the
* purpose of its {@link BlankNode#uniqueReference()}.
*
* @see #asTriple(RDF, org.apache.jena.graph.Triple)
*
* @param triple
* Jena {@link org.apache.jena.graph.Triple} to adapt
* @return Adapted {@link JenaTriple}
* @throws ConversionException
* if any of the triple's nodes are not concrete or the triple
* is a generalized triple
*/
public JenaTriple asTriple(final org.apache.jena.graph.Triple triple) throws ConversionException {
return INTERNAL_JENA_FACTORY.createTriple(triple, salt());
}
/**
* Adapt a generalized Jena {@link org.apache.jena.graph.Triple} to a
* CommonsRDF {@link TripleLike}.
* <p>
* The generalized triple supports any {@link RDFTerm} as its
* {@link TripleLike#getSubject()} {@link TripleLike#getPredicate()} or
* {@link TripleLike#getObject()}.
* <p>
* If the Jena triple contains any {@link Node#isBlank()}, then any
* corresponding {@link BlankNode} will use a {@link UUID} salt from this
* {@link JenaRDF} instance in combination with
* {@link Node#getBlankNodeId()} for the purpose of its
* {@link BlankNode#uniqueReference()}.
*
* @see #asTriple(RDF, org.apache.jena.graph.Triple)
*
* @param triple
* Jena triple
* @return Adapted {@link TripleLike}. Note that the generalized triple does
* <strong>not</strong> implement {@link Triple#equals(Object)} or
* {@link Triple#hashCode()}.
* @throws ConversionException
* if any of the triple's nodes are not concrete
*/
public JenaTripleLike asGeneralizedTriple(final org.apache.jena.graph.Triple triple) throws ConversionException {
return INTERNAL_JENA_FACTORY.createGeneralizedTriple(triple, salt());
}
/**
* Adapt a generalized Jena {@link org.apache.jena.sparql.core.Quad} to a
* CommonsRDF {@link QuadLike}.
* <p>
* The generalized quad supports any {@link RDFTerm} as its
* {@link QuadLike#getGraphName()}, {@link QuadLike#getSubject()}
* {@link QuadLike#getPredicate()} or {@link QuadLike#getObject()}.
* <p>
* If the Jena quad contains any {@link Node#isBlank()}, then any
* corresponding {@link BlankNode} will use a {@link UUID} salt from this
* {@link JenaRDF} instance in combination with
* {@link Node#getBlankNodeId()} for the purpose of its
* {@link BlankNode#uniqueReference()}.
* <p>
* If the provided quad {@link org.apache.jena.sparql.core.Quad#isDefaultGraph()},
* the returned {@link JenaQuadLike} has a {@link JenaQuadLike#getGraphName()}
* of {@link Optional#empty()}.
*
* @see #asQuad(org.apache.jena.sparql.core.Quad)
* @see #asGeneralizedTriple(org.apache.jena.graph.Triple)
*
* @param quad
* Jena quad
* @return Adapted {@link QuadLike}. Note that the generalized quad does
* <strong>not</strong> implement {@link Quad#equals(Object)} or
* {@link Quad#hashCode()}.
* @throws ConversionException
* if any of the quad nodes are not concrete
*/
public JenaQuadLike<RDFTerm> asGeneralizedQuad(final org.apache.jena.sparql.core.Quad quad) throws ConversionException {
return INTERNAL_JENA_FACTORY.createGeneralizedQuad(quad, salt());
}
/**
* Convert from Jena {@link org.apache.jena.graph.Triple} to a Commons RDF
* {@link Triple}.
* <p>
* Note that if any of the triple's nodes {@link Node#isBlank()}, then the
* factory's {@link RDF#createBlankNode(String)} will be used, meaning that
* care should be taken if reusing an {@link RDF} instance for multiple
* conversion sessions.
*
* @see #asTriple(org.apache.jena.graph.Triple)
*
* @param factory
* {@link RDF} to use for creating the {@link Triple} and its
* {@link RDFTerm}s.
* @param triple
* Jena triple
* @return Converted triple
* @throws ConversionException
* if any of the triple's nodes are not concrete or the triple
* is a generalized triple
*/
public static Triple asTriple(final RDF factory, final org.apache.jena.graph.Triple triple) throws ConversionException {
if (factory instanceof JenaRDF) {
// No need to convert, just wrap
return ((JenaRDF) factory).asTriple(triple);
}
final BlankNodeOrIRI subject;
final IRI predicate;
try {
subject = (BlankNodeOrIRI) asRDFTerm(factory, triple.getSubject());
predicate = (IRI) asRDFTerm(factory, triple.getPredicate());
} catch (final ClassCastException ex) {
throw new ConversionException("Can't convert generalized triple: " + triple, ex);
}
final RDFTerm object = asRDFTerm(factory, triple.getObject());
return factory.createTriple(subject, predicate, object);
}
/**
* Adapt an existing Jena {@link org.apache.jena.sparql.core.Quad} to
* CommonsRDF {@link Quad}.
* <p>
* If the quad contains any {@link Node#isBlank()}, then any corresponding
* {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF}
* instance in combination with {@link Node#getBlankNodeId()} for the
* purpose of its {@link BlankNode#uniqueReference()}.
* <p>
* If the provided quad {@link org.apache.jena.sparql.core.Quad#isDefaultGraph()},
* the returned {@link JenaQuad} has a {@link Quad#getGraphName()}
* of {@link Optional#empty()}.
*
* @param quad
* Jena quad
* @return Adapted quad
*/
public JenaQuad asQuad(final org.apache.jena.sparql.core.Quad quad) {
return INTERNAL_JENA_FACTORY.createQuad(quad, salt());
}
/**
* Adapt an existing Jena {@link org.apache.jena.graph.Graph} to CommonsRDF
* {@link Graph}.
* <p>
* This does not take a copy, changes to the CommonsRDF Graph are reflected
* in the jena graph, which is accessible from
* {@link JenaGraph#asJenaGraph()}.
* <p>
* If the graph contains any {@link Node#isBlank()}, then any corresponding
* {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF}
* instance in combination with {@link Node#getBlankNodeId()} for the
* purpose of its {@link BlankNode#uniqueReference()}.
*
* @param graph
* Jena {@link org.apache.jena.graph.Graph} to adapt
* @return Adapted {@link JenaGraph}
*/
public JenaGraph asGraph(final org.apache.jena.graph.Graph graph) {
return INTERNAL_JENA_FACTORY.createGraph(graph, salt());
}
/**
* Adapt an existing Jena {@link org.apache.jena.rdf.model.Model} to
* CommonsRDF {@link Graph}.
* <p>
* This does not ake a copy, changes to the CommonsRDF Graph are reflected
* in the jena graph, which is accessible from
* {@link JenaGraph#asJenaGraph()}.
* <p>
* If the graph contains any {@link Node#isBlank()}, then any corresponding
* {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF}
* instance in combination with {@link Node#getBlankNodeId()} for the
* purpose of its {@link BlankNode#uniqueReference()}.
*
* @param model
* Jena {@link org.apache.jena.rdf.model.Model} to adapt
* @return Adapted {@link JenaGraph}
*/
public JenaGraph asGraph(final org.apache.jena.rdf.model.Model model) {
return INTERNAL_JENA_FACTORY.createGraph(model, salt());
}
/**
* Adapt an existing Jena {@link DatasetGraph} to CommonsRDF
* {@link Dataset}.
* <p>
* This does not take a copy, changes to the CommonsRDF Dataset are
* reflected in the jena dataset graph, which is accessible from
* {@link JenaDataset#asJenaDatasetGraph()}.
* <p>
* If the dataset contains any {@link Node#isBlank()}, then any
* corresponding {@link BlankNode} will use a {@link UUID} salt from this
* {@link JenaRDF} instance in combination with
* {@link Node#getBlankNodeId()} for the purpose of its
* {@link BlankNode#uniqueReference()}.
*
* @param datasetGraph
* Jena {@link DatasetGraph} to adapt
* @return Adapted {@link JenaDataset}
*/
public JenaDataset asDataset(final DatasetGraph datasetGraph) {
return INTERNAL_JENA_FACTORY.createDataset(datasetGraph, salt());
}
/**
* Adapt an existing Jena {@link org.apache.jena.query.Dataset} to
* CommonsRDF {@link Dataset}.
* <p>
* This does not take a copy, changes to the CommonsRDF Dataset are
* reflected in the jena dataset graph, which is accessible from
* {@link JenaDataset#asJenaDatasetGraph()}.
* <p>
* If the dataset contains any {@link Node#isBlank()}, then any
* corresponding {@link BlankNode} will use a {@link UUID} salt from this
* {@link JenaRDF} instance in combination with
* {@link Node#getBlankNodeId()} for the purpose of its
* {@link BlankNode#uniqueReference()}.
*
* @param datasetGraph
* Jena {@link org.apache.jena.query.Dataset} to adapt
* @return Adapted {@link JenaDataset}
*/
public JenaDataset asDataset(final org.apache.jena.query.Dataset datasetGraph) {
return INTERNAL_JENA_FACTORY.createDataset(datasetGraph.asDatasetGraph(), salt());
}
/**
* Convert from Jena {@link org.apache.jena.sparql.core.Quad} to a Commons
* RDF {@link Quad}.
* <p>
* Note that if any of the quad's nodes {@link Node#isBlank()}, then the
* factory's {@link RDF#createBlankNode(String)} will be used, meaning that
* care should be taken if reusing an {@link RDF} instance for multiple
* conversion sessions.
* <p>
* If the provided quad {@link org.apache.jena.sparql.core.Quad#isDefaultGraph()},
* the returned {@link JenaQuadLike} has a {@link JenaQuadLike#getGraphName()}
* of {@link Optional#empty()}.
*
* @see #asQuad(org.apache.jena.sparql.core.Quad)
* @see #asGeneralizedQuad(org.apache.jena.sparql.core.Quad)
*
* @param factory
* {@link RDF} to use for creating the {@link Triple} and its
* {@link RDFTerm}s.
* @param quad
* Jena {@link org.apache.jena.sparql.core.Quad} to adapt
* @return Converted {@link Quad}
* @throws ConversionException
* if any of the quad's nodes are not concrete or the quad is a
* generalized quad
*/
public static Quad asQuad(final RDF factory, final org.apache.jena.sparql.core.Quad quad) {
if (factory instanceof JenaRDF) {
// No need to convert, just wrap
return ((JenaRDF) factory).asQuad(quad);
}
final BlankNodeOrIRI graphName = (BlankNodeOrIRI) (asRDFTerm(factory, quad.getGraph()));
final BlankNodeOrIRI subject = (BlankNodeOrIRI) (asRDFTerm(factory, quad.getSubject()));
final IRI predicate = (IRI) (asRDFTerm(factory, quad.getPredicate()));
final RDFTerm object = asRDFTerm(factory, quad.getObject());
return factory.createQuad(graphName, subject, predicate, object);
}
/**
* Return {@link RDFSyntax} corresponding to a Jena {@link Lang}.
*
* @param lang
* {@link Lang} to convert
* @return Matched {@link RDFSyntax}, otherwise {@link Optional#empty()}
*/
public Optional<RDFSyntax> asRDFSyntax(final Lang lang) {
return RDFSyntax.byMediaType(lang.getContentType().getContentType());
}
/**
* Return Jena {@link Lang} corresponding to a {@link RDFSyntax}.
*
* @param rdfSyntax
* {@link RDFSyntax} to convert
* @return Matched {@link Lang}, otherwise {@link Optional#empty()}
*/
public Optional<Lang> asJenaLang(final RDFSyntax rdfSyntax) {
return Optional.ofNullable(RDFLanguages.contentTypeToLang(rdfSyntax.mediaType()));
}
/**
* Create a {@link StreamRDF} instance that inserts the converted
* {@link Quad}s. into a the provided {@link Consumer}.
* <p>
* The returned {@link StreamRDF} can be used for instance with Jena's
* {@link RDFDataMgr#parse(StreamRDF, String)}.
*
* @param factory
* {@link RDF} to use for creating {@link RDFTerm}s and
* {@link Quad}s.
* @param consumer
* A {@link Consumer} of {@link Quad}s
* @return A {@link StreamRDF} that will stream converted quads to the
* consumer
*/
public static StreamRDF streamJenaToQuad(final RDF factory, final Consumer<Quad> consumer) {
return new StreamRDFBase() {
@Override
public void quad(final org.apache.jena.sparql.core.Quad quad) {
consumer.accept(asQuad(factory, quad));
}
};
}
/**
* Create a {@link StreamRDF} instance that inserts generalized
* {@link TripleLike}s. into a the provided {@link Consumer}.
* <p>
* A generalized triple allows any {@link RDFTerm} for
* {@link TripleLike#getSubject()}, {@link TripleLike#getPredicate()} and
* {@link TripleLike#getObject()}.
* <p>
* The returned {@link StreamRDF} can be used for instance with Jena's
* {@link RDFDataMgr#parse(StreamRDF, String)}.
*
* @param generalizedConsumer
* A {@link Consumer} of generalized {@link TripleLike}s
* @return A {@link StreamRDF} that will stream generalized triples to the
* consumer
*/
public StreamRDF streamJenaToGeneralizedTriple(final Consumer<TripleLike> generalizedConsumer) {
return new StreamRDFBase() {
@Override
public void triple(final org.apache.jena.graph.Triple triple) {
generalizedConsumer.accept(asGeneralizedTriple(triple));
}
};
}
/**
* Create a {@link StreamRDF} instance that inserts generalized
* {@link QuadLike}s. into a the provided {@link Consumer}.
* <p>
* A generalized quad allows any {@link RDFTerm} for
* {@link QuadLike#getSubject()}, {@link TripleLike#getPredicate()},
* {@link QuadLike#getObject()} and {@link QuadLike#getGraphName()} .
* <p>
* The returned {@link StreamRDF} can be used for instance with Jena's
* {@link RDFDataMgr#parse(StreamRDF, String)}.
*
* @param generalizedConsumer
* A {@link Consumer} of generalized {@link QuadLike}s
* @return A {@link StreamRDF} that will stream generalized quads to the
* consumer
*/
public StreamRDF streamJenaToGeneralizedQuad(final Consumer<QuadLike<RDFTerm>> generalizedConsumer) {
return new StreamRDFBase() {
@Override
public void quad(final org.apache.jena.sparql.core.Quad quad) {
generalizedConsumer.accept(asGeneralizedQuad(quad));
}
};
}
/**
* Convert a CommonsRDF Dataset to a Jena Dataset. If the Dataset was from Jena
* originally, return that original object wrapped else create a copy using Jena
* objects.
*
* @param dataset
* Commons RDF {@link Dataset} to convert
* @return Converted Jena {@link org.apache.jena.query.Dataset}
*/
public org.apache.jena.query.Dataset asJenaDataset(final Dataset dataset) {
return DatasetFactory.wrap(asJenaDatasetGraph(dataset));
}
/**
* Convert a CommonsRDF Dataset to a Jena DatasetGraph. If the Dataset was from Jena
* originally, return that original object's underlying DatasetGraph else create a
* copy using Jena objects.
*
* @param dataset
* Commons RDF {@link Dataset} to convert
* @return Converted Jena {@link org.apache.jena.sparql.core.DatasetGraph}
*/
public DatasetGraph asJenaDatasetGraph(final Dataset dataset) {
final DatasetGraph dsg;
if (dataset instanceof JenaDataset) {
dsg = ((JenaDataset) dataset).asJenaDatasetGraph();
} else {
dsg = DatasetGraphFactory.createGeneral();
dataset.stream().map(this::asJenaQuad).forEach(dsg::add);
}
return dsg;
}
/**
* Convert a CommonsRDF Graph to a Jena Graph. If the Graph was from Jena
* originally, return that original object else create a copy using Jena
* objects.
*
* @param graph
* Commons RDF {@link Graph} to convert
* @return Converted Jena {@link org.apache.jena.graph.Graph}
*/
public org.apache.jena.graph.Graph asJenaGraph(final Graph graph) {
if (graph instanceof JenaGraph) {
return ((JenaGraph) graph).asJenaGraph();
}
final org.apache.jena.graph.Graph g = GraphFactory.createGraphMem();
graph.stream().forEach(t -> g.add(asJenaTriple(t)));
return g;
}
/**
* Convert a CommonsRDF RDFTerm to a Jena Node. If the RDFTerm was from Jena
* originally, return that original object, else create a copy using Jena
* objects.
*
* @param term
* Commons RDF {@link RDFTerm} to convert
* @return Converted Jena {@link Node}
*/
public Node asJenaNode(final RDFTerm term) {
if (term == null) {
return null;
}
if (term instanceof JenaRDFTerm) {
// TODO: What if it's a JenaBlankNodeImpl with
// a different salt? Do we need to rewrite the
// jena blanknode identifier?
return ((JenaRDFTerm) term).asJenaNode();
}
if (term instanceof IRI) {
return NodeFactory.createURI(((IRI) term).getIRIString());
}
if (term instanceof Literal) {
final Literal lit = (Literal) term;
final RDFDatatype dt = NodeFactory.getType(lit.getDatatype().getIRIString());
final String lang = lit.getLanguageTag().orElse("");
return NodeFactory.createLiteral(lit.getLexicalForm(), lang, dt);
}
if (term instanceof BlankNode) {
final String id = ((BlankNode) term).uniqueReference();
return NodeFactory.createBlankNode(id);
}
throw new ConversionException("Not a concrete RDF Term: " + term);
}
/**
* Convert a CommonsRDF {@link Triple} to a Jena
* {@link org.apache.jena.graph.Triple}.
* <p>
* If the triple was from Jena originally, return that original object, else
* create a copy using Jena objects.
*
* @param triple
* Commons RDF {@link Triple} to convert
* @return Converted Jena {@link org.apache.jena.graph.Triple}
*/
public org.apache.jena.graph.Triple asJenaTriple(final Triple triple) {
if (triple instanceof JenaTriple) {
return ((JenaTriple) triple).asJenaTriple();
}
return org.apache.jena.graph.Triple.create(asJenaNode(triple.getSubject()),
asJenaNode(triple.getPredicate()),
asJenaNode(triple.getObject()));
}
/**
* Convert a CommonsRDF {@link Quad} to a Jena
* {@link org.apache.jena.sparql.core.Quad}.
* <p>
* If the quad was from Jena originally, return that original object,
* otherwise create a copy using Jena objects.
*
* @param quad
* Commons RDF {@link Quad} to convert
* @return Converted Jena {@link org.apache.jena.sparql.core.Quad}
*/
public org.apache.jena.sparql.core.Quad asJenaQuad(final Quad quad) {
if (quad instanceof JenaQuad) {
return ((JenaQuad) quad).asJenaQuad();
}
return org.apache.jena.sparql.core.Quad.create(
asJenaNode(quad.getGraphName().orElse(null)),
asJenaNode(quad.getSubject()),
asJenaNode(quad.getPredicate()),
asJenaNode(quad.getObject()));
}
// Some simple validations - full IRI parsing is not cheap.
private void validateIRI(final String iri) {
if (iri.contains(" ")) {
throw new IllegalArgumentException();
}
if (iri.contains("<")) {
throw new IllegalArgumentException();
}
if (iri.contains(">")) {
throw new IllegalArgumentException();
}
}
private static void validateLang(final String languageTag) {
if (languageTag.contains(" ")) {
throw new IllegalArgumentException("Invalid language tag: " + languageTag);
}
}
/**
* Return the {@link UUID} salt used by this factory.
* <p>
* The salt is used for the purposes of {@link BlankNode} identity, see
* {@link BlankNode#uniqueReference()} for details.
* <p>
* This salt can be used with the constructor {@link JenaRDF#JenaRDF(UUID)}
* if consistent or reproducible {@link BlankNode}s are desirable.
*
* @return The {@link UUID} used as salt
*/
public UUID salt() {
return salt;
}
}