001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.commons.rdf.jena;
019
020import java.util.Optional;
021import java.util.UUID;
022import java.util.function.Consumer;
023
024import org.apache.commons.rdf.api.BlankNode;
025import org.apache.commons.rdf.api.BlankNodeOrIRI;
026import org.apache.commons.rdf.api.Dataset;
027import org.apache.commons.rdf.api.Graph;
028import org.apache.commons.rdf.api.IRI;
029import org.apache.commons.rdf.api.Literal;
030import org.apache.commons.rdf.api.Quad;
031import org.apache.commons.rdf.api.QuadLike;
032import org.apache.commons.rdf.api.RDFSyntax;
033import org.apache.commons.rdf.api.RDFTerm;
034import org.apache.commons.rdf.api.RDF;
035import org.apache.commons.rdf.api.Triple;
036import org.apache.commons.rdf.api.TripleLike;
037import org.apache.commons.rdf.jena.impl.InternalJenaFactory;
038import org.apache.jena.datatypes.RDFDatatype;
039import org.apache.jena.datatypes.xsd.XSDDatatype;
040import org.apache.jena.graph.Node;
041import org.apache.jena.graph.NodeFactory;
042import org.apache.jena.query.DatasetFactory;
043import org.apache.jena.riot.Lang;
044import org.apache.jena.riot.RDFDataMgr;
045import org.apache.jena.riot.RDFLanguages;
046import org.apache.jena.riot.system.StreamRDF;
047import org.apache.jena.riot.system.StreamRDFBase;
048import org.apache.jena.sparql.core.DatasetGraph;
049import org.apache.jena.sparql.core.DatasetGraphFactory;
050import org.apache.jena.sparql.graph.GraphFactory;
051
052/**
053 * Apache Jena RDF implementation.
054 * <p>
055 * Instances of JenaRDF can also convert existing objects from Jena with methods
056 * like {@link #asRDFTerm(Node)} and
057 * {@link #asGraph(org.apache.jena.graph.Graph)}, and vice versa from any
058 * Commons RDF object to Jena with the <code>asJena*</code> methods like
059 * {@link #asJenaNode(RDFTerm)} and {@link #asJenaGraph(Graph)}.
060 * <p>
061 * Note that Commons RDF objects created by this class implement the
062 * specializations interfaces like {@link JenaRDFTerm}, {@link JenaGraph} and
063 * {@link JenaTriple}, which provide access to the underlying Jena objects, e.g.
064 * with {@link JenaRDFTerm#asJenaNode()}.
065 * <p>
066 * For the purpose of {@link BlankNode} identity when using
067 * {@link #createBlankNode(String)} (see {@link BlankNode#equals(Object)} and
068 * {@link BlankNode#uniqueReference()}), each instance of JenaRDF uses an
069 * internal random state. If for some reason consistent/reproducible BlankNode
070 * identity is desired, it is possible to retrieve the state as a UUID using
071 * {@link #salt()} for subsequent use with {@link JenaRDF#JenaRDF(UUID)} - note
072 * that such consistency is only guaranteed within the same minor version of
073 * Commons RDF.
074 *
075 * @see RDF
076 */
077public final class JenaRDF implements RDF {
078
079    private static InternalJenaFactory internalJenaFactory = new InternalJenaFactory() {
080    };
081
082    private final UUID salt;
083
084    /**
085     * Create a JenaRDF.
086     * <p>
087     * This constructor will use a randomly generated {@link UUID} as a salt for
088     * the purposes of {@link BlankNode} identity, see {@link #salt()}.
089     */
090    public JenaRDF() {
091        this.salt = UUID.randomUUID();
092    }
093
094    /**
095     * Create a JenaRDF.
096     * <p>
097     * This constructor will use the specified {@link UUID} as a salt for the
098     * purposes of {@link BlankNode} identity, and should only be used in cases
099     * where predictable and consistent {@link BlankNode#uniqueReference()} are
100     * important.
101     *
102     * @param salt
103     *            {@link UUID} to use as salt for {@link BlankNode} equality
104     */
105    public JenaRDF(final UUID salt) {
106        this.salt = salt;
107    }
108
109    @Override
110    public JenaBlankNode createBlankNode() {
111        return internalJenaFactory.createBlankNode(salt());
112    }
113
114    @Override
115    public JenaBlankNode createBlankNode(final String name) {
116        return internalJenaFactory.createBlankNode(name, salt());
117    }
118
119    @Override
120    public JenaDataset createDataset() {
121        return internalJenaFactory.createDataset(salt());
122    }
123
124    @Override
125    public JenaGraph createGraph() {
126        return internalJenaFactory.createGraph(salt());
127    }
128
129    @Override
130    public JenaIRI createIRI(final String iri) {
131        validateIRI(iri);
132        return internalJenaFactory.createIRI(iri);
133    }
134
135    @Override
136    public JenaLiteral createLiteral(final String lexicalForm) {
137        return internalJenaFactory.createLiteral(lexicalForm);
138    }
139
140    @Override
141    public JenaLiteral createLiteral(final String lexicalForm, final IRI dataType) {
142        return internalJenaFactory.createLiteralDT(lexicalForm, dataType.getIRIString());
143    }
144
145    @Override
146    public JenaLiteral createLiteral(final String lexicalForm, final String languageTag) {
147        validateLang(languageTag);
148        return internalJenaFactory.createLiteralLang(lexicalForm, languageTag);
149    }
150
151    @Override
152    public JenaTriple createTriple(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
153        return internalJenaFactory.createTriple(subject, predicate, object);
154    }
155
156    /**
157     * {@inheritDoc}
158     * <p>
159     * In addition to supporting a <code>graphName</code> of <code>null</code>
160     * for representing a triple in the <em>default graph</em>, this method also
161     * recognize a {@link JenaIRI} which {@link JenaRDFTerm#asJenaNode()}
162     * represent the default graph according to
163     * {@link org.apache.jena.sparql.core.Quad#isDefaultGraph(Node)}, in which
164     * case the returned JenaQuad will have a {@link Quad#getGraphName()} of
165     * {@link Optional#empty()} rather than the provided IRI.
166     *
167     */
168    @Override
169    public JenaQuad createQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object)
170            throws IllegalArgumentException, UnsupportedOperationException {
171        return internalJenaFactory.createQuad(subject, predicate, object, graphName);
172    }
173
174    /**
175     * Create a generalized Jena triple.
176     * <p>
177     * The <em>generalized triple</em> supports any {@link RDFTerm} as its
178     * {@link TripleLike#getSubject()} {@link TripleLike#getPredicate()} or
179     * {@link TripleLike#getObject()}.
180     *
181     * @see #createTriple(BlankNodeOrIRI, IRI, RDFTerm)
182     * @see #createGeneralizedQuad(RDFTerm, RDFTerm, RDFTerm, RDFTerm)
183     *
184     * @param subject
185     *            The subject of the statement
186     * @param predicate
187     *            The predicate of the statement
188     * @param object
189     *            The object of the statement
190     * @return Generalized {@link TripleLike}. Note that the generalized triple
191     *         does <strong>not</strong> implement {@link Triple#equals(Object)}
192     *         or {@link Triple#hashCode()}.
193     */
194    public JenaGeneralizedTripleLike createGeneralizedTriple(final RDFTerm subject, final RDFTerm predicate, final RDFTerm object) {
195        return internalJenaFactory.createGeneralizedTriple(subject, predicate, object);
196    }
197
198    /**
199     * Create a generalized Jena quad.
200     * <p>
201     * The <em>generalized quad</em> supports any {@link RDFTerm} as its
202     * {@link QuadLike#getSubject()} {@link QuadLike#getPredicate()},
203     * {@link QuadLike#getObject()} or {@link QuadLike#getObject()}.
204     * <p>
205     * In addition to supporting a <code>graphName</code> of <code>null</code>
206     * for representing a triple in the <em>default graph</em>, this method also
207     * recognize a {@link JenaIRI} which {@link JenaRDFTerm#asJenaNode()}
208     * represent the default graph according to
209     * {@link org.apache.jena.sparql.core.Quad#isDefaultGraph(Node)}, in which
210     * case the returned JenaQuad will have a {@link Quad#getGraphName()} of
211     * {@link Optional#empty()} rather than the provided IRI.
212     *
213     * @see #createQuad(BlankNodeOrIRI, BlankNodeOrIRI, IRI, RDFTerm)
214     * @see #createGeneralizedTriple(RDFTerm, RDFTerm, RDFTerm)
215     *
216     * @param subject
217     *            The subject of the statement
218     * @param predicate
219     *            The predicate of the statement
220     * @param object
221     *            The object of the statement
222     * @param graphName
223     *            The graph name of the statement
224     * @return Generalized {@link QuadLike}. Note that the generalized quad does
225     *         <strong>not</strong> implement {@link Quad#equals(Object)} or
226     *         {@link Quad#hashCode()}.
227     */
228    public JenaGeneralizedQuadLike createGeneralizedQuad(final RDFTerm subject, final RDFTerm predicate, final RDFTerm object,
229            final RDFTerm graphName) {
230        return internalJenaFactory.createGeneralizedQuad(subject, predicate, object, graphName);
231    }
232
233    /**
234     * Adapt an existing Jena Node to CommonsRDF {@link RDFTerm}.
235     * <p>
236     * If {@link Node#isLiteral()}, then the returned value is a
237     * {@link Literal}. If {@link Node#isURI()}, the returned value is a IRI. If
238     * {$@link Node#isBlank()}, the returned value is a {@link BlankNode}, which
239     * will use a {@link UUID} salt from this {@link JenaRDF} instance in
240     * combination with {@link Node#getBlankNodeId()} for the purpose of its
241     * {@link BlankNode#uniqueReference()}.
242     *
243     * @see #asRDFTerm(RDF, Node)
244     *
245     * @param node
246     *            The Jena Node to adapt. It's {@link Node#isConcrete()} must be
247     *            <code>true</code>.
248     * @return Adapted {@link JenaRDFTerm}
249     * @throws ConversionException
250     *             If the {@link Node} can't be represented as an
251     *             {@link RDFTerm}, e.g. if the node is not concrete or
252     *             represents a variable in Jena.
253     */
254    public JenaRDFTerm asRDFTerm(final Node node) throws ConversionException {
255        return internalJenaFactory.createRDFTerm(node, salt());
256    }
257
258    /**
259     * Convert from Jena {@link Node} to any Commons RDF implementation.
260     * <p>
261     * Note that if the {@link Node#isBlank()}, then the factory's
262     * {@link RDF#createBlankNode(String)} will be used, meaning that care
263     * should be taken if reusing an {@link RDF} instance for multiple
264     * conversion sessions.
265     *
266     * @see #asRDFTerm(Node)
267     *
268     * @param factory
269     *            {@link RDF} to use for creating {@link RDFTerm}.
270     * @param node
271     *            The Jena Node to adapt. It's {@link Node#isConcrete()} must be
272     *            <code>true</code>.
273     * @return Adapted {@link RDFTerm}
274     * @throws ConversionException
275     *             If the {@link Node} can't be represented as an
276     *             {@link RDFTerm}, e.g. if the node is not concrete or
277     *             represents a variable in Jena.
278     */
279    public static RDFTerm asRDFTerm(final RDF factory, final Node node) {
280        if (node == null) {
281            return null;
282        }
283        if (factory instanceof JenaRDF) {
284            // No need to convert, just wrap
285            return ((JenaRDF) factory).asRDFTerm(node);
286        }
287        if (node.isURI()) {
288            return factory.createIRI(node.getURI());
289        }
290        if (node.isLiteral()) {
291            final String lang = node.getLiteralLanguage();
292            if (lang != null && !lang.isEmpty()) {
293                return factory.createLiteral(node.getLiteralLexicalForm(), lang);
294            }
295            if (node.getLiteralDatatype().equals(XSDDatatype.XSDstring)) {
296                return factory.createLiteral(node.getLiteralLexicalForm());
297            }
298            final IRI dt = factory.createIRI(node.getLiteralDatatype().getURI());
299            return factory.createLiteral(node.getLiteralLexicalForm(), dt);
300        }
301        if (node.isBlank()) {
302            // The factory
303            return factory.createBlankNode(node.getBlankNodeLabel());
304        }
305        throw new ConversionException("Node is not a concrete RDF Term: " + node);
306    }
307
308    /**
309     * Adapt an existing Jena Triple to CommonsRDF {@link Triple}.
310     * <p>
311     * If the triple contains any {@link Node#isBlank()}, then any corresponding
312     * {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF}
313     * instance in combination with {@link Node#getBlankNodeId()} for the
314     * purpose of its {@link BlankNode#uniqueReference()}.
315     *
316     * @see #asTriple(RDF, org.apache.jena.graph.Triple)
317     *
318     * @param triple
319     *            Jena {@link org.apache.jena.graph.Triple} to adapt
320     * @return Adapted {@link JenaTriple}
321     * @throws ConversionException
322     *             if any of the triple's nodes are not concrete or the triple
323     *             is a generalized triple
324     */
325    public JenaTriple asTriple(final org.apache.jena.graph.Triple triple) throws ConversionException {
326        return internalJenaFactory.createTriple(triple, salt());
327    }
328
329    /**
330     * Adapt a generalized Jena {@link org.apache.jena.graph.Triple} to a
331     * CommonsRDF {@link TripleLike}.
332     * <p>
333     * The generalized triple supports any {@link RDFTerm} as its
334     * {@link TripleLike#getSubject()} {@link TripleLike#getPredicate()} or
335     * {@link TripleLike#getObject()}.
336     * <p>
337     * If the Jena triple contains any {@link Node#isBlank()}, then any
338     * corresponding {@link BlankNode} will use a {@link UUID} salt from this
339     * {@link JenaRDF} instance in combination with
340     * {@link Node#getBlankNodeId()} for the purpose of its
341     * {@link BlankNode#uniqueReference()}.
342     *
343     * @see #asTriple(RDF, org.apache.jena.graph.Triple)
344     *
345     * @param triple
346     *            Jena triple
347     * @return Adapted {@link TripleLike}. Note that the generalized triple does
348     *         <strong>not</strong> implement {@link Triple#equals(Object)} or
349     *         {@link Triple#hashCode()}.
350     * @throws ConversionException
351     *             if any of the triple's nodes are not concrete
352     */
353    public JenaTripleLike asGeneralizedTriple(final org.apache.jena.graph.Triple triple) throws ConversionException {
354        return internalJenaFactory.createGeneralizedTriple(triple, salt());
355    }
356
357    /**
358     * Adapt a generalized Jena {@link org.apache.jena.sparql.core.Quad} to a
359     * CommonsRDF {@link QuadLike}.
360     * <p>
361     * The generalized quad supports any {@link RDFTerm} as its
362     * {@link QuadLike#getGraphName()}, {@link QuadLike#getSubject()}
363     * {@link QuadLike#getPredicate()} or {@link QuadLike#getObject()}.
364     * <p>
365     * If the Jena quad contains any {@link Node#isBlank()}, then any
366     * corresponding {@link BlankNode} will use a {@link UUID} salt from this
367     * {@link JenaRDF} instance in combination with
368     * {@link Node#getBlankNodeId()} for the purpose of its
369     * {@link BlankNode#uniqueReference()}.
370     * <p>
371     * If the provided quad {@link org.apache.jena.sparql.core.Quad#isDefaultGraph()},
372     * the returned {@link JenaQuadLike} has a {@link JenaQuadLike#getGraphName()}
373     * of {@link Optional#empty()}.
374     *
375     * @see #asQuad(org.apache.jena.sparql.core.Quad)
376     * @see #asGeneralizedTriple(org.apache.jena.graph.Triple)
377     *
378     * @param quad
379     *            Jena quad
380     * @return Adapted {@link QuadLike}. Note that the generalized quad does
381     *         <strong>not</strong> implement {@link Quad#equals(Object)} or
382     *         {@link Quad#hashCode()}.
383     * @throws ConversionException
384     *             if any of the quad nodes are not concrete
385     */
386    public JenaQuadLike<RDFTerm> asGeneralizedQuad(final org.apache.jena.sparql.core.Quad quad) throws ConversionException {
387        return internalJenaFactory.createGeneralizedQuad(quad, salt());
388    }
389
390    /**
391     * Convert from Jena {@link org.apache.jena.graph.Triple} to a Commons RDF
392     * {@link Triple}.
393     * <p>
394     * Note that if any of the triple's nodes {@link Node#isBlank()}, then the
395     * factory's {@link RDF#createBlankNode(String)} will be used, meaning that
396     * care should be taken if reusing an {@link RDF} instance for multiple
397     * conversion sessions.
398     *
399     * @see #asTriple(org.apache.jena.graph.Triple)
400     *
401     * @param factory
402     *            {@link RDF} to use for creating the {@link Triple} and its
403     *            {@link RDFTerm}s.
404     * @param triple
405     *            Jena triple
406     * @return Converted triple
407     * @throws ConversionException
408     *             if any of the triple's nodes are not concrete or the triple
409     *             is a generalized triple
410     */
411    public static Triple asTriple(final RDF factory, final org.apache.jena.graph.Triple triple) throws ConversionException {
412        if (factory instanceof JenaRDF) {
413            // No need to convert, just wrap
414            return ((JenaRDF) factory).asTriple(triple);
415        }
416        final BlankNodeOrIRI subject;
417        final IRI predicate;
418        try {
419            subject = (BlankNodeOrIRI) asRDFTerm(factory, triple.getSubject());
420            predicate = (IRI) asRDFTerm(factory, triple.getPredicate());
421        } catch (final ClassCastException ex) {
422            throw new ConversionException("Can't convert generalized triple: " + triple, ex);
423        }
424        final RDFTerm object = asRDFTerm(factory, triple.getObject());
425        return factory.createTriple(subject, predicate, object);
426    }
427
428    /**
429     * Adapt an existing Jena {@link org.apache.jena.sparql.core.Quad} to
430     * CommonsRDF {@link Quad}.
431     * <p>
432     * If the quad contains any {@link Node#isBlank()}, then any corresponding
433     * {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF}
434     * instance in combination with {@link Node#getBlankNodeId()} for the
435     * purpose of its {@link BlankNode#uniqueReference()}.
436     * <p>
437     * If the provided quad {@link org.apache.jena.sparql.core.Quad#isDefaultGraph()},
438     * the returned {@link JenaQuad} has a {@link Quad#getGraphName()}
439     * of {@link Optional#empty()}.
440     *
441     * @param quad
442     *            Jena quad
443     * @return Adapted quad
444     */
445    public JenaQuad asQuad(final org.apache.jena.sparql.core.Quad quad) {
446        return internalJenaFactory.createQuad(quad, salt());
447    }
448
449    /**
450     * Adapt an existing Jena {@link org.apache.jena.graph.Graph} to CommonsRDF
451     * {@link Graph}.
452     * <p>
453     * This does not take a copy, changes to the CommonsRDF Graph are reflected
454     * in the jena graph, which is accessible from
455     * {@link JenaGraph#asJenaGraph()}.
456     * <p>
457     * If the graph contains any {@link Node#isBlank()}, then any corresponding
458     * {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF}
459     * instance in combination with {@link Node#getBlankNodeId()} for the
460     * purpose of its {@link BlankNode#uniqueReference()}.
461     *
462     * @param graph
463     *            Jena {@link org.apache.jena.graph.Graph} to adapt
464     * @return Adapted {@link JenaGraph}
465     */
466    public JenaGraph asGraph(final org.apache.jena.graph.Graph graph) {
467        return internalJenaFactory.createGraph(graph, salt());
468    }
469
470    /**
471     * Adapt an existing Jena {@link org.apache.jena.rdf.model.Model} to
472     * CommonsRDF {@link Graph}.
473     * <p>
474     * This does not ake a copy, changes to the CommonsRDF Graph are reflected
475     * in the jena graph, which is accessible from
476     * {@link JenaGraph#asJenaGraph()}.
477     * <p>
478     * If the graph contains any {@link Node#isBlank()}, then any corresponding
479     * {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF}
480     * instance in combination with {@link Node#getBlankNodeId()} for the
481     * purpose of its {@link BlankNode#uniqueReference()}.
482     *
483     * @param model
484     *            Jena {@link org.apache.jena.rdf.model.Model} to adapt
485     * @return Adapted {@link JenaGraph}
486     */
487    public JenaGraph asGraph(final org.apache.jena.rdf.model.Model model) {
488        return internalJenaFactory.createGraph(model, salt());
489    }
490
491    /**
492     * Adapt an existing Jena {@link DatasetGraph} to CommonsRDF
493     * {@link Dataset}.
494     * <p>
495     * This does not take a copy, changes to the CommonsRDF Dataset are
496     * reflected in the jena dataset graph, which is accessible from
497     * {@link JenaDataset#asJenaDatasetGraph()}.
498     * <p>
499     * If the dataset contains any {@link Node#isBlank()}, then any
500     * corresponding {@link BlankNode} will use a {@link UUID} salt from this
501     * {@link JenaRDF} instance in combination with
502     * {@link Node#getBlankNodeId()} for the purpose of its
503     * {@link BlankNode#uniqueReference()}.
504     *
505     * @param datasetGraph
506     *            Jena {@link DatasetGraph} to adapt
507     * @return Adapted {@link JenaDataset}
508     */
509    public JenaDataset asDataset(final DatasetGraph datasetGraph) {
510        return internalJenaFactory.createDataset(datasetGraph, salt());
511    }
512
513    /**
514     * Adapt an existing Jena {@link org.apache.jena.query.Dataset} to
515     * CommonsRDF {@link Dataset}.
516     * <p>
517     * This does not take a copy, changes to the CommonsRDF Dataset are
518     * reflected in the jena dataset graph, which is accessible from
519     * {@link JenaDataset#asJenaDatasetGraph()}.
520     * <p>
521     * If the dataset contains any {@link Node#isBlank()}, then any
522     * corresponding {@link BlankNode} will use a {@link UUID} salt from this
523     * {@link JenaRDF} instance in combination with
524     * {@link Node#getBlankNodeId()} for the purpose of its
525     * {@link BlankNode#uniqueReference()}.
526     *
527     * @param datasetGraph
528     *            Jena {@link org.apache.jena.query.Dataset} to adapt
529     * @return Adapted {@link JenaDataset}
530     */
531    public JenaDataset asDataset(final org.apache.jena.query.Dataset datasetGraph) {
532        return internalJenaFactory.createDataset(datasetGraph.asDatasetGraph(), salt());
533    }
534
535    /**
536     * Convert from Jena {@link org.apache.jena.sparql.core.Quad} to a Commons
537     * RDF {@link Quad}.
538     * <p>
539     * Note that if any of the quad's nodes {@link Node#isBlank()}, then the
540     * factory's {@link RDF#createBlankNode(String)} will be used, meaning that
541     * care should be taken if reusing an {@link RDF} instance for multiple
542     * conversion sessions.
543     * <p>
544     * If the provided quad {@link org.apache.jena.sparql.core.Quad#isDefaultGraph()},
545     * the returned {@link JenaQuadLike} has a {@link JenaQuadLike#getGraphName()}
546     * of {@link Optional#empty()}.
547     *
548     * @see #asQuad(org.apache.jena.sparql.core.Quad)
549     * @see #asGeneralizedQuad(org.apache.jena.sparql.core.Quad)
550     *
551     * @param factory
552     *            {@link RDF} to use for creating the {@link Triple} and its
553     *            {@link RDFTerm}s.
554     * @param quad
555     *            Jena {@link org.apache.jena.sparql.core.Quad} to adapt
556     * @return Converted {@link Quad}
557     * @throws ConversionException
558     *             if any of the quad's nodes are not concrete or the quad is a
559     *             generalized quad
560     */
561    public static Quad asQuad(final RDF factory, final org.apache.jena.sparql.core.Quad quad) {
562        if (factory instanceof JenaRDF) {
563            // No need to convert, just wrap
564            return ((JenaRDF) factory).asQuad(quad);
565        }
566        final BlankNodeOrIRI graphName = (BlankNodeOrIRI) (asRDFTerm(factory, quad.getGraph()));
567        final BlankNodeOrIRI subject = (BlankNodeOrIRI) (asRDFTerm(factory, quad.getSubject()));
568        final IRI predicate = (IRI) (asRDFTerm(factory, quad.getPredicate()));
569        final RDFTerm object = asRDFTerm(factory, quad.getObject());
570        return factory.createQuad(graphName, subject, predicate, object);
571    }
572
573    /**
574     * Return {@link RDFSyntax} corresponding to a Jena {@link Lang}.
575     *
576     * @param lang
577     *            {@link Lang} to convert
578     * @return Matched {@link RDFSyntax}, otherwise {@link Optional#empty()}
579     */
580    public Optional<RDFSyntax> asRDFSyntax(final Lang lang) {
581        return RDFSyntax.byMediaType(lang.getContentType().getContentType());
582    }
583
584    /**
585     * Return Jena {@link Lang} corresponding to a {@link RDFSyntax}.
586     *
587     * @param rdfSyntax
588     *            {@link RDFSyntax} to convert
589     * @return Matched {@link Lang}, otherwise {@link Optional#empty()}
590     */
591    public Optional<Lang> asJenaLang(final RDFSyntax rdfSyntax) {
592        return Optional.ofNullable(RDFLanguages.contentTypeToLang(rdfSyntax.mediaType()));
593    }
594
595    /**
596     * Create a {@link StreamRDF} instance that inserts the converted
597     * {@link Quad}s. into a the provided {@link Consumer}.
598     * <p>
599     * The returned {@link StreamRDF} can be used for instance with Jena's
600     * {@link RDFDataMgr#parse(StreamRDF, String)}.
601     *
602     * @param factory
603     *            {@link RDF} to use for creating {@link RDFTerm}s and
604     *            {@link Quad}s.
605     * @param consumer
606     *            A {@link Consumer} of {@link Quad}s
607     * @return A {@link StreamRDF} that will stream converted quads to the
608     *         consumer
609     */
610    public static StreamRDF streamJenaToQuad(final RDF factory, final Consumer<Quad> consumer) {
611        return new StreamRDFBase() {
612            @Override
613            public void quad(final org.apache.jena.sparql.core.Quad quad) {
614                consumer.accept(asQuad(factory, quad));
615            }
616        };
617    }
618
619    /**
620     * Create a {@link StreamRDF} instance that inserts generalized
621     * {@link TripleLike}s. into a the provided {@link Consumer}.
622     * <p>
623     * A generalized triple allows any {@link RDFTerm} for
624     * {@link TripleLike#getSubject()}, {@link TripleLike#getPredicate()} and
625     * {@link TripleLike#getObject()}.
626     * <p>
627     * The returned {@link StreamRDF} can be used for instance with Jena's
628     * {@link RDFDataMgr#parse(StreamRDF, String)}.
629     *
630     * @param generalizedConsumer
631     *            A {@link Consumer} of generalized {@link TripleLike}s
632     * @return A {@link StreamRDF} that will stream generalized triples to the
633     *         consumer
634     */
635    public StreamRDF streamJenaToGeneralizedTriple(final Consumer<TripleLike> generalizedConsumer) {
636        return new StreamRDFBase() {
637            @Override
638            public void triple(final org.apache.jena.graph.Triple triple) {
639                generalizedConsumer.accept(asGeneralizedTriple(triple));
640            }
641        };
642    }
643
644    /**
645     * Create a {@link StreamRDF} instance that inserts generalized
646     * {@link QuadLike}s. into a the provided {@link Consumer}.
647     * <p>
648     * A generalized quad allows any {@link RDFTerm} for
649     * {@link QuadLike#getSubject()}, {@link TripleLike#getPredicate()},
650     * {@link QuadLike#getObject()} and {@link QuadLike#getGraphName()} .
651     * <p>
652     * The returned {@link StreamRDF} can be used for instance with Jena's
653     * {@link RDFDataMgr#parse(StreamRDF, String)}.
654     *
655     * @param generalizedConsumer
656     *            A {@link Consumer} of generalized {@link QuadLike}s
657     * @return A {@link StreamRDF} that will stream generalized quads to the
658     *         consumer
659     */
660    public StreamRDF streamJenaToGeneralizedQuad(final Consumer<QuadLike<RDFTerm>> generalizedConsumer) {
661        return new StreamRDFBase() {
662            @Override
663            public void quad(final org.apache.jena.sparql.core.Quad quad) {
664                generalizedConsumer.accept(asGeneralizedQuad(quad));
665            }
666        };
667    }
668
669    /**
670     * Convert a CommonsRDF Dataset to a Jena Dataset. If the Dataset was from Jena
671     * originally, return that original object wrapped else create a copy using Jena
672     * objects.
673     *
674     * @param dataset
675     *            Commons RDF {@link Dataset} to convert
676     * @return Converted Jena {@link org.apache.jena.query.Dataset}
677     */
678    public org.apache.jena.query.Dataset asJenaDataset(final Dataset dataset) {
679        return DatasetFactory.wrap(asJenaDatasetGraph(dataset));
680    }
681
682    /**
683     * Convert a CommonsRDF Dataset to a Jena DatasetGraph. If the Dataset was from Jena
684     * originally, return that original object's underlying DatasetGraph else create a
685     * copy using Jena objects.
686     *
687     * @param dataset
688     *            Commons RDF {@link Dataset} to convert
689     * @return Converted Jena {@link org.apache.jena.sparql.core.DatasetGraph}
690     */
691    public DatasetGraph asJenaDatasetGraph(final Dataset dataset) {
692        final DatasetGraph dsg;
693        if (dataset instanceof JenaDataset)
694            dsg = ((JenaDataset) dataset).asJenaDatasetGraph();
695        else {
696            dsg = DatasetGraphFactory.createGeneral();
697            dataset.stream().map(this::asJenaQuad).forEach(dsg::add);
698        }
699        return dsg;
700    }
701
702    /**
703     * Convert a CommonsRDF Graph to a Jena Graph. If the Graph was from Jena
704     * originally, return that original object else create a copy using Jena
705     * objects.
706     *
707     * @param graph
708     *            Commons RDF {@link Graph} to convert
709     * @return Converted Jena {@link org.apache.jena.graph.Graph}
710     */
711    public org.apache.jena.graph.Graph asJenaGraph(final Graph graph) {
712        if (graph instanceof JenaGraph) {
713            return ((JenaGraph) graph).asJenaGraph();
714        }
715        final org.apache.jena.graph.Graph g = GraphFactory.createGraphMem();
716        graph.stream().forEach(t -> g.add(asJenaTriple(t)));
717        return g;
718    }
719
720    /**
721     * Convert a CommonsRDF RDFTerm to a Jena Node. If the RDFTerm was from Jena
722     * originally, return that original object, else create a copy using Jena
723     * objects.
724     *
725     * @param term
726     *            Commons RDF {@link RDFTerm} to convert
727     * @return Converted Jena {@link Node}
728     */
729    public Node asJenaNode(final RDFTerm term) {
730        if (term == null) {
731            return null;
732        }
733        if (term instanceof JenaRDFTerm) {
734            // TODO: What if it's a JenaBlankNodeImpl with
735            // a different salt? Do we need to rewrite the
736            // jena blanknode identifier?
737            return ((JenaRDFTerm) term).asJenaNode();
738        }
739
740        if (term instanceof IRI) {
741            return NodeFactory.createURI(((IRI) term).getIRIString());
742        }
743
744        if (term instanceof Literal) {
745            final Literal lit = (Literal) term;
746            final RDFDatatype dt = NodeFactory.getType(lit.getDatatype().getIRIString());
747            final String lang = lit.getLanguageTag().orElse("");
748            return NodeFactory.createLiteral(lit.getLexicalForm(), lang, dt);
749        }
750
751        if (term instanceof BlankNode) {
752            final String id = ((BlankNode) term).uniqueReference();
753            return NodeFactory.createBlankNode(id);
754        }
755        throw new ConversionException("Not a concrete RDF Term: " + term);
756    }
757
758    /**
759     * Convert a CommonsRDF {@link Triple} to a Jena
760     * {@link org.apache.jena.graph.Triple}.
761     * <p>
762     * If the triple was from Jena originally, return that original object, else
763     * create a copy using Jena objects.
764     *
765     * @param triple
766     *            Commons RDF {@link Triple} to convert
767     * @return Converted Jena {@link org.apache.jena.graph.Triple}
768     */
769    public org.apache.jena.graph.Triple asJenaTriple(final Triple triple) {
770        if (triple instanceof JenaTriple) {
771            return ((JenaTriple) triple).asJenaTriple();
772        }
773        return org.apache.jena.graph.Triple.create(asJenaNode(triple.getSubject()),
774                asJenaNode(triple.getPredicate()),
775                asJenaNode(triple.getObject()));
776    }
777
778    /**
779     * Convert a CommonsRDF {@link Quad} to a Jena
780     * {@link org.apache.jena.sparql.core.Quad}.
781     * <p>
782     * If the quad was from Jena originally, return that original object,
783     * otherwise create a copy using Jena objects.
784     *
785     * @param quad
786     *            Commons RDF {@link Quad} to convert
787     * @return Converted Jena {@link org.apache.jena.sparql.core.Quad}
788     */
789    public org.apache.jena.sparql.core.Quad asJenaQuad(final Quad quad) {
790        if (quad instanceof JenaQuad) {
791            return ((JenaQuad) quad).asJenaQuad();
792        }
793        return org.apache.jena.sparql.core.Quad.create(
794                asJenaNode(quad.getGraphName().orElse(null)),
795                asJenaNode(quad.getSubject()),
796                asJenaNode(quad.getPredicate()),
797                asJenaNode(quad.getObject()));
798    }
799
800    // Some simple validations - full IRI parsing is not cheap.
801    private void validateIRI(final String iri) {
802        if (iri.contains(" ")) {
803            throw new IllegalArgumentException();
804        }
805        if (iri.contains("<")) {
806            throw new IllegalArgumentException();
807        }
808        if (iri.contains(">")) {
809            throw new IllegalArgumentException();
810        }
811    }
812
813    private static void validateLang(final String languageTag) {
814        if (languageTag.contains(" ")) {
815            throw new IllegalArgumentException("Invalid language tag: " + languageTag);
816        }
817    }
818
819    /**
820     * Return the {@link UUID} salt used by this factory.
821     * <p>
822     * The salt is used for the purposes of {@link BlankNode} identity, see
823     * {@link BlankNode#uniqueReference()} for details.
824     * <p>
825     * This salt can be used with the constructor {@link JenaRDF#JenaRDF(UUID)}
826     * if consistent or reproducible {@link BlankNode}s are desirable.
827     *
828     * @return The {@link UUID} used as salt
829     */
830    public UUID salt() {
831        return salt;
832    }
833
834}