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.rdf4j;
019
020import java.util.Arrays;
021import java.util.EnumSet;
022import java.util.Objects;
023import java.util.Set;
024import java.util.UUID;
025
026// To avoid confusion, avoid importing
027// classes that are in both
028// commons.rdf and openrdf.model (e.g. IRI, Literal)
029import org.apache.commons.rdf.api.BlankNode;
030import org.apache.commons.rdf.api.BlankNodeOrIRI;
031import org.apache.commons.rdf.api.Dataset;
032import org.apache.commons.rdf.api.Graph;
033import org.apache.commons.rdf.api.Quad;
034import org.apache.commons.rdf.api.RDFTerm;
035import org.apache.commons.rdf.api.RDF;
036import org.apache.commons.rdf.api.Triple;
037import org.apache.commons.rdf.api.TripleLike;
038import org.apache.commons.rdf.rdf4j.impl.InternalRDF4JFactory;
039import org.eclipse.rdf4j.model.BNode;
040import org.eclipse.rdf4j.model.IRI;
041import org.eclipse.rdf4j.model.Literal;
042import org.eclipse.rdf4j.model.Model;
043import org.eclipse.rdf4j.model.Resource;
044import org.eclipse.rdf4j.model.Statement;
045import org.eclipse.rdf4j.model.Value;
046import org.eclipse.rdf4j.model.ValueFactory;
047import org.eclipse.rdf4j.model.impl.LinkedHashModel;
048import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
049import org.eclipse.rdf4j.repository.Repository;
050import org.eclipse.rdf4j.repository.RepositoryConnection;
051import org.eclipse.rdf4j.repository.sail.SailRepository;
052import org.eclipse.rdf4j.sail.Sail;
053import org.eclipse.rdf4j.sail.memory.MemoryStore;
054
055/**
056 * RDF4J implementation of RDF.
057 * <p>
058 * The {@link #RDF4J()} constructor uses a {@link SimpleValueFactory} to create
059 * corresponding RDF4J {@link Value} instances. Alternatively, this factory can
060 * be constructed with a different {@link ValueFactory} using
061 * {@link #RDF4J(ValueFactory)}.
062 * <p>
063 * {@link #asRDFTerm(Value)} can be used to convert any RDF4J {@link Value} to
064 * an RDFTerm. Note that adapted {@link BNode}s are considered equal if they are
065 * converted with the same {@link RDF4J} instance and have the same
066 * {@link BNode#getID()}.
067 * <p>
068 * {@link #createGraph()} creates a new Graph backed by {@link LinkedHashModel}.
069 * To use other models, see {@link #asGraph(Model)}.
070 * <p>
071 * To adapt a RDF4J {@link Repository} as a {@link Dataset} or {@link Graph},
072 * use {@link #asDataset(Repository, Option...)} or
073 * {@link #asGraph(Repository, Option...)}.
074 * <p>
075 * {@link #asTriple(Statement)} can be used to convert a RDF4J {@link Statement}
076 * to a Commons RDF {@link Triple}, and equivalent {@link #asQuad(Statement)} to
077 * convert a {@link Quad}.
078 * <p>
079 * To convert any {@link Triple} or {@link Quad} to to RDF4J {@link Statement},
080 * use {@link #asStatement(TripleLike)}. This recognises previously converted
081 * {@link RDF4JTriple}s and {@link RDF4JQuad}s without re-converting their
082 * {@link RDF4JTripleLike#asStatement()}.
083 * <p>
084 * Likewise, {@link #asValue(RDFTerm)} can be used to convert any Commons RDF
085 * {@link RDFTerm} to a corresponding RDF4J {@link Value}. This recognises
086 * previously converted {@link RDF4JTerm}s without re-converting their
087 * {@link RDF4JTerm#asValue()}.
088 * <p>
089 * For the purpose of {@link BlankNode} equivalence, this factory contains an
090 * internal {@link UUID} salt that is used by adapter methods like
091 * {@link #asQuad(Statement)}, {@link #asTriple(Statement)},
092 * {@link #asRDFTerm(Value)} as well as {@link #createBlankNode(String)}. As
093 * RDF4J {@link BNode} instances from multiple repositories or models may have
094 * the same {@link BNode#getID()}, converting them with the above methods might
095 * cause accidental {@link BlankNode} equivalence. Note that the {@link Graph}
096 * and {@link Dataset} adapter methods like
097 * {@link #asDataset(Repository, Option...)} and
098 * {@link #asGraph(Repository, Option...)} therefore uses a unique {@link RDF4J}
099 * internally.
100 *
101 * @see RDF
102 *
103 */
104public final class RDF4J implements RDF {
105
106    /**
107     * InternalRDF4JFactory is deliberately abstract
108     */
109    private static InternalRDF4JFactory rdf4j = new InternalRDF4JFactory() {
110    };
111
112    public enum Option {
113        /**
114         * The Graph/Dataset should include any inferred statements
115         */
116        includeInferred,
117        /**
118         * The graph/dataset should handle {@link Repository#initialize()} (if
119         * needed) and {@link Repository#shutDown()} on {@link Graph#close()} /
120         * {@link Dataset#close()}.
121         */
122        handleInitAndShutdown
123    }
124
125    private final UUID salt;
126
127    private final ValueFactory valueFactory;
128
129    /**
130     * Construct an {@link RDF4J}.
131     *
132     */
133    public RDF4J() {
134        this(SimpleValueFactory.getInstance(), UUID.randomUUID());
135    }
136
137    /**
138     * Construct an {@link RDF4J}.
139     * <p>
140     * This constructor is intended for use with the value factory from
141     * {@link Repository#getValueFactory()} when using Repository-based graphs
142     * and datasets.
143     *
144     * @param valueFactory
145     *            The RDF4J {@link ValueFactory} to use
146     */
147    public RDF4J(final ValueFactory valueFactory) {
148        this(valueFactory, UUID.randomUUID());
149    }
150
151    /**
152     * Construct an {@link RDF4J}.
153     * <p>
154     * This constructor may be used if reproducible
155     * {@link BlankNode#uniqueReference()} in {@link BlankNode} is desirable.
156     *
157     * @param salt
158     *            An {@link UUID} salt to be used by any created
159     *            {@link BlankNode}s for the purpose of
160     *            {@link BlankNode#uniqueReference()}
161     */
162    public RDF4J(final UUID salt) {
163        this(SimpleValueFactory.getInstance(), salt);
164    }
165
166    /**
167     * Construct an {@link RDF4J}.
168     * <p>
169     * This constructor may be used if reproducible
170     * {@link BlankNode#uniqueReference()} in {@link BlankNode} is desirable.
171     *
172     * @param valueFactory
173     *            The RDF4J {@link ValueFactory} to use
174     * @param salt
175     *            An {@link UUID} salt to be used by any created
176     *            {@link BlankNode}s for the purpose of
177     *            {@link BlankNode#uniqueReference()}
178     */
179    public RDF4J(final ValueFactory valueFactory, final UUID salt) {
180        this.valueFactory = valueFactory;
181        this.salt = salt;
182    }
183
184    /**
185     * Adapt a RDF4J {@link Statement} as a Commons RDF {@link Quad}.
186     * <p>
187     * For the purpose of {@link BlankNode} equivalence, this method will use an
188     * internal salt UUID that is unique per instance of {@link RDF4J}.
189     * <p>
190     * <strong>NOTE:</strong> If combining RDF4J {@link Statement}s multiple
191     * repositories or models, then their {@link BNode}s may have the same
192     * {@link BNode#getID()}, which with this method would become equivalent
193     * according to {@link BlankNode#equals(Object)} and
194     * {@link BlankNode#uniqueReference()}, unless a separate {@link RDF4J}
195     * instance is used per RDF4J repository/model.
196     *
197     * @param statement
198     *            The statement to convert
199     * @return A {@link RDF4JQuad} that is equivalent to the statement
200     */
201    public RDF4JQuad asQuad(final Statement statement) {
202        return rdf4j.createQuadImpl(statement, salt);
203    }
204
205    /**
206     *
207     * Adapt a RDF4J {@link Value} as a Commons RDF {@link RDFTerm}.
208     * <p>
209     * The value will be of the same kind as the term, e.g. a
210     * {@link org.eclipse.rdf4j.model.BNode} is converted to a
211     * {@link org.apache.commons.rdf.api.BlankNode}, a
212     * {@link org.eclipse.rdf4j.model.IRI} is converted to a
213     * {@link org.apache.commons.rdf.api.IRI} and a
214     * {@link org.eclipse.rdf4j.model.Literal}. is converted to a
215     * {@link org.apache.commons.rdf.api.Literal}
216     * <p>
217     * For the purpose of {@link BlankNode} equivalence, this method will use an
218     * internal salt UUID that is unique per instance of {@link RDF4J}.
219     * <p>
220     * <strong>NOTE:</strong> If combining RDF4J values from multiple
221     * repositories or models, then their {@link BNode}s may have the same
222     * {@link BNode#getID()}, which with this method would become equivalent
223     * according to {@link BlankNode#equals(Object)} and
224     * {@link BlankNode#uniqueReference()}, unless a separate {@link RDF4J}
225     * instance is used per RDF4J repository/model.
226     *
227     * @param value
228     *            The RDF4J {@link Value} to convert.
229     * @return A {@link RDFTerm} that corresponds to the RDF4J value
230     * @throws IllegalArgumentException
231     *             if the value is not a BNode, Literal or IRI
232     */
233    public RDF4JTerm asRDFTerm(final Value value) {
234        return asRDFTerm(value, salt);
235    }
236
237    /**
238     *
239     * Adapt a RDF4J
240     * {@link org.eclipse.rdf4j.model.BNode} as a Commons RDF
241     * {@link org.apache.commons.rdf.api.BlankNode}
242     * <p>
243     * For the purpose of {@link BlankNode} equivalence, this method will use an
244     * internal salt UUID that is unique per instance of {@link RDF4J}.
245     * <p>
246     * <strong>NOTE:</strong> If combining RDF4J values from multiple
247     * repositories or models, then their {@link BNode}s may have the same
248     * {@link BNode#getID()}, which with this method would become equivalent
249     * according to {@link BlankNode#equals(Object)} and
250     * {@link BlankNode#uniqueReference()}, unless a separate {@link RDF4J}
251     * instance is used per RDF4J repository/model.
252     *
253     * @param value
254     *            The RDF4J {@link BNode} to convert.
255     * @return A {@link RDF4JBlankNode} that corresponds to the RDF4J BNode
256     */
257    public RDF4JBlankNode asRDFTerm(final BNode value) {
258        return rdf4j.createBlankNodeImpl(value, salt);
259    }
260
261    /**
262     *
263     * Adapt a RDF4J
264     * {@link org.eclipse.rdf4j.model.Literal} as a Commons RDF
265     * {@link org.apache.commons.rdf.api.Literal}
266     * <p>
267     * @param value
268     *            The RDF4J {@link Literal} to convert.
269     * @return A {@link RDF4JLiteral} that corresponds to the RDF4J literal
270     */
271    public RDF4JLiteral asRDFTerm(final Literal value) {
272        return rdf4j.createLiteralImpl(value);
273    }
274
275    /**
276     *
277     * Adapt a RDF4J
278     * {@link org.eclipse.rdf4j.model.IRI} as a Commons RDF
279     * {@link org.apache.commons.rdf.api.IRI}
280     * <p>
281     * @param value
282     *            The RDF4J {@link Value} to convert.
283     * @return A {@link RDF4JIRI} that corresponds to the RDF4J IRI
284     */
285    public RDF4JIRI asRDFTerm(final org.eclipse.rdf4j.model.IRI value) {
286        return rdf4j.createIRIImpl(value);
287    }
288
289    /**
290     *
291     * Adapt a RDF4J
292     * {@link org.eclipse.rdf4j.model.Resource} as a Commons RDF
293     * {@link org.apache.commons.rdf.api.BlankNodeOrIRI}
294     * <p>
295     * @param value
296     *            The RDF4J {@link Value} to convert.
297     * @return A {@link RDF4JBlankNodeOrIRI} that corresponds to the RDF4J Resource
298     */
299    public RDF4JBlankNodeOrIRI asRDFTerm(final org.eclipse.rdf4j.model.Resource value) {
300        if(value instanceof IRI){
301            return asRDFTerm((IRI)value);
302        } else if (value instanceof BNode){
303            return asRDFTerm((BNode)value);
304        }
305        throw new IllegalArgumentException("Value is not a BNode or IRI: " + value.getClass());
306    }
307
308    /**
309     * Adapt a RDF4J {@link Value} as a Commons RDF {@link RDFTerm}.
310     * <p>
311     * The value will be of the same kind as the term, e.g. a
312     * {@link org.eclipse.rdf4j.model.BNode} is converted to a
313     * {@link org.apache.commons.rdf.api.BlankNode}, a
314     * {@link org.eclipse.rdf4j.model.IRI} is converted to a
315     * {@link org.apache.commons.rdf.api.IRI} and a
316     * {@link org.eclipse.rdf4j.model.Literal}. is converted to a
317     * {@link org.apache.commons.rdf.api.Literal}
318     *
319     * @param value
320     *            The RDF4J {@link Value} to convert.
321     * @param salt
322     *            A {@link UUID} salt to use for uniquely mapping any
323     *            {@link BNode}s. The salt should typically be the same for
324     *            multiple statements in the same {@link Repository} or
325     *            {@link Model} to ensure {@link BlankNode#equals(Object)} and
326     *            {@link BlankNode#uniqueReference()} works as intended.
327     * @return A {@link RDFTerm} that corresponds to the RDF4J value
328     * @throws IllegalArgumentException
329     *             if the value is not a BNode, Literal or IRI
330     */
331    public static RDF4JTerm asRDFTerm(final Value value, final UUID salt) {
332        if (value instanceof BNode) {
333            return rdf4j.createBlankNodeImpl((BNode) value, salt);
334        }
335        if (value instanceof org.eclipse.rdf4j.model.Literal) {
336            return rdf4j.createLiteralImpl((org.eclipse.rdf4j.model.Literal) value);
337        }
338        if (value instanceof org.eclipse.rdf4j.model.IRI) {
339            return rdf4j.createIRIImpl((org.eclipse.rdf4j.model.IRI) value);
340        }
341        throw new IllegalArgumentException("Value is not a BNode, Literal or IRI: " + value.getClass());
342    }
343
344    /**
345     * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Dataset}.
346     * <p>
347     * Changes to the dataset are reflected in the repository, and vice versa.
348     * <p>
349     * <strong>Note:</strong> Some operations on the {@link RDF4JDataset}
350     * requires the use of try-with-resources to close underlying
351     * {@link RepositoryConnection}s, including {@link RDF4JDataset#iterate()},
352     * {@link RDF4JDataset#stream()} and {@link RDF4JDataset#getGraphNames()}.
353     *
354     * @param repository
355     *            RDF4J {@link Repository} to connect to.
356     * @param options
357     *            Zero or more {@link Option}
358     * @return A {@link Dataset} backed by the RDF4J repository.
359     */
360    public RDF4JDataset asDataset(final Repository repository, final Option... options) {
361        final EnumSet<Option> opts = optionSet(options);
362        return rdf4j.createRepositoryDatasetImpl(repository, opts.contains(Option.handleInitAndShutdown),
363                opts.contains(Option.includeInferred));
364    }
365
366    /**
367     * Adapt an RDF4J {@link Model} as a Commons RDF {@link Graph}.
368     * <p>
369     * Changes to the graph are reflected in the model, and vice versa.
370     *
371     * @param model
372     *            RDF4J {@link Model} to adapt.
373     * @return Adapted {@link Graph}.
374     */
375    public RDF4JGraph asGraph(final Model model) {
376        return rdf4j.createModelGraphImpl(model, this);
377    }
378
379    /**
380     * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Graph}.
381     * <p>
382     * The graph will only include triples in the default graph (equivalent to
383     * context <code>new Resource[0]{null})</code> in RDF4J).
384     * <p>
385     * Changes to the graph are reflected in the repository, and vice versa.
386     * <p>
387     * <strong>Note:</strong> Some operations on the {@link RDF4JGraph} requires
388     * the use of try-with-resources to close underlying
389     * {@link RepositoryConnection}s, including {@link RDF4JGraph#iterate()} and
390     * {@link RDF4JGraph#stream()}.
391     *
392     * @param repository
393     *            RDF4J {@link Repository} to connect to.
394     * @param options
395     *            Zero or more {@link Option}
396     * @return A {@link Graph} backed by the RDF4J repository.
397     */
398    public RDF4JGraph asGraph(final Repository repository, final Option... options) {
399        final EnumSet<Option> opts = optionSet(options);
400        return rdf4j.createRepositoryGraphImpl(repository, opts.contains(Option.handleInitAndShutdown),
401                opts.contains(Option.includeInferred), new Resource[] { null }); // default
402                                                                                 // graph
403    }
404
405    /**
406     * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Graph}.
407     * <p>
408     * The graph will include triples in any contexts (e.g. the union graph).
409     * <p>
410     * Changes to the graph are reflected in the repository, and vice versa.
411     *
412     * @param repository
413     *            RDF4J {@link Repository} to connect to.
414     * @param options
415     *            Zero or more {@link Option}
416     * @return A union {@link Graph} backed by the RDF4J repository.
417     */
418    public RDF4JGraph asGraphUnion(final Repository repository, final Option... options) {
419        final EnumSet<Option> opts = optionSet(options);
420        return rdf4j.createRepositoryGraphImpl(repository, opts.contains(Option.handleInitAndShutdown),
421                opts.contains(Option.includeInferred), new Resource[] {}); // union
422                                                                           // graph
423
424    }
425
426    /**
427     * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Graph}.
428     * <p>
429     * The graph will include triples in the specified contexts.
430     * <p>
431     * Changes to the graph are reflected in the repository, and vice versa.
432     * Triples added/removed to the graph are reflected in all the specified
433     * contexts.
434     * <p>
435     * <strong>Note:</strong> Some operations on the {@link RDF4JGraph} requires
436     * the use of try-with-resources to close underlying
437     * {@link RepositoryConnection}s, including {@link RDF4JGraph#iterate()} and
438     * {@link RDF4JGraph#stream()}.
439     *
440     * @param repository
441     *            RDF4J {@link Repository} to connect to.
442     * @param contexts
443     *            A {@link Set} of {@link BlankNodeOrIRI} specifying the graph
444     *            names to use as a context. The set may include the value
445     *            <code>null</code> to indicate the default graph. The empty set
446     *            indicates any context, e.g. the <em>union graph</em>.
447     * @param option
448     *            Zero or more {@link Option}s
449     * @return A {@link Graph} backed by the RDF4J repository.
450     */
451    public RDF4JGraph asGraph(final Repository repository, final Set<? extends BlankNodeOrIRI> contexts, final Option... option) {
452        final EnumSet<Option> opts = optionSet(option);
453        /** NOTE: asValue() deliberately CAN handle <code>null</code> */
454        final Resource[] resources = contexts.stream().map(g -> (Resource) asValue(g)).toArray(Resource[]::new);
455        return rdf4j.createRepositoryGraphImpl(Objects.requireNonNull(repository),
456                opts.contains(Option.handleInitAndShutdown), opts.contains(Option.includeInferred), resources);
457    }
458
459    /**
460     * Adapt a Commons RDF {@link Triple} or {@link Quad} as a RDF4J
461     * {@link Statement}.
462     * <p>
463     * If the <code>tripleLike</code> argument is an {@link RDF4JTriple} or a
464     * {@link RDF4JQuad}, then its {@link RDF4JTripleLike#asStatement()} is
465     * returned as-is. Note that this means that a {@link RDF4JTriple} would
466     * preserve its {@link Statement#getContext()}, and that any
467     * {@link BlankNode}s would be deemed equivalent in RDF4J if they have the
468     * same {@link BNode#getID()}.
469     *
470     * @param tripleLike
471     *            A {@link Triple} or {@link Quad} to adapt
472     * @return A corresponding {@link Statement}
473     */
474    public Statement asStatement(final TripleLike tripleLike) {
475        if (tripleLike instanceof RDF4JTripleLike) {
476            // Return original statement - this covers both RDF4JQuad and
477            // RDF4JTriple
478            return ((RDF4JTripleLike) tripleLike).asStatement();
479        }
480
481        final org.eclipse.rdf4j.model.Resource subject = (org.eclipse.rdf4j.model.Resource) asValue(tripleLike.getSubject());
482        final org.eclipse.rdf4j.model.IRI predicate = (org.eclipse.rdf4j.model.IRI) asValue(tripleLike.getPredicate());
483        final Value object = asValue(tripleLike.getObject());
484
485        org.eclipse.rdf4j.model.Resource context = null;
486        if (tripleLike instanceof Quad) {
487            final Quad quad = (Quad) tripleLike;
488            context = (org.eclipse.rdf4j.model.Resource) asValue(quad.getGraphName().orElse(null));
489        }
490
491        return getValueFactory().createStatement(subject, predicate, object, context);
492    }
493
494    /**
495     * Adapt a RDF4J {@link Statement} as a Commons RDF {@link Triple}.
496     * <p>
497     * For the purpose of {@link BlankNode} equivalence, this method will use an
498     * internal salt UUID that is unique per instance of {@link RDF4J}.
499     * <p>
500     * <strong>NOTE:</strong> If combining RDF4J statements from multiple
501     * repositories or models, then their {@link BNode}s may have the same
502     * {@link BNode#getID()}, which with this method would become equivalent
503     * according to {@link BlankNode#equals(Object)} and
504     * {@link BlankNode#uniqueReference()}, unless a separate {@link RDF4J}
505     * instance is used per RDF4J repository/model.
506     *
507     * @param statement
508     *            The RDF4J {@link Statement} to adapt.
509     * @return A {@link RDF4JTriple} that is equivalent to the statement
510     */
511    public RDF4JTriple asTriple(final Statement statement) {
512        return rdf4j.createTripleImpl(statement, salt);
513    }
514
515    /**
516     * Adapt a Commons RDF {@link RDFTerm} as a RDF4J {@link Value}.
517     * <p>
518     * The value will be of the same kind as the term, e.g. a
519     * {@link org.apache.commons.rdf.api.BlankNode} is converted to a
520     * {@link org.eclipse.rdf4j.model.BNode}, a
521     * {@link org.apache.commons.rdf.api.IRI} is converted to a
522     * {@link org.eclipse.rdf4j.model.IRI} and a
523     * {@link org.apache.commons.rdf.api.Literal} is converted to a
524     * {@link org.eclipse.rdf4j.model.Literal}.
525     * <p>
526     * If the provided {@link RDFTerm} is <code>null</code>, then the returned
527     * value is <code>null</code>.
528     * <p>
529     * If the provided term is an instance of {@link RDF4JTerm}, then the
530     * {@link RDF4JTerm#asValue()} is returned without any conversion. Note that
531     * this could mean that a {@link Value} from a different kind of
532     * {@link ValueFactory} could be returned.
533     *
534     * @param term
535     *            RDFTerm to adapt to RDF4J Value
536     * @return Adapted RDF4J {@link Value}
537     */
538    public Value asValue(final RDFTerm term) {
539        if (term == null) {
540            return null;
541        }
542        if (term instanceof RDF4JTerm) {
543            // One of our own - avoid converting again.
544            // (This is crucial to avoid double-escaping in BlankNode)
545            return ((RDF4JTerm) term).asValue();
546        }
547        if (term instanceof org.apache.commons.rdf.api.IRI) {
548            final org.apache.commons.rdf.api.IRI iri = (org.apache.commons.rdf.api.IRI) term;
549            return getValueFactory().createIRI(iri.getIRIString());
550        }
551        if (term instanceof org.apache.commons.rdf.api.Literal) {
552            final org.apache.commons.rdf.api.Literal literal = (org.apache.commons.rdf.api.Literal) term;
553            final String label = literal.getLexicalForm();
554            if (literal.getLanguageTag().isPresent()) {
555                final String lang = literal.getLanguageTag().get();
556                return getValueFactory().createLiteral(label, lang);
557            }
558            final org.eclipse.rdf4j.model.IRI dataType = (org.eclipse.rdf4j.model.IRI) asValue(literal.getDatatype());
559            return getValueFactory().createLiteral(label, dataType);
560        }
561        if (term instanceof BlankNode) {
562            // This is where it gets tricky to support round trips!
563            final BlankNode blankNode = (BlankNode) term;
564            // FIXME: The uniqueReference might not be a valid BlankNode
565            // identifier..
566            // does it have to be in RDF4J?
567            return getValueFactory().createBNode(blankNode.uniqueReference());
568        }
569        throw new IllegalArgumentException("RDFTerm was not an IRI, Literal or BlankNode: " + term.getClass());
570    }
571
572    @Override
573    public RDF4JBlankNode createBlankNode() {
574        final BNode bnode = getValueFactory().createBNode();
575        return asRDFTerm(bnode);
576    }
577
578    @Override
579    public RDF4JBlankNode createBlankNode(final String name) {
580        final BNode bnode = getValueFactory().createBNode(name);
581        return asRDFTerm(bnode);
582    }
583
584    /**
585     * {@inheritDoc}
586     * <p>
587     * <strong>Note:</strong> Some operations on the {@link RDF4JDataset}
588     * requires the use of try-with-resources to close underlying
589     * {@link RepositoryConnection}s, including {@link RDF4JDataset#iterate()},
590     * {@link RDF4JDataset#stream()} and {@link RDF4JDataset#getGraphNames()}.
591     *
592     */
593    @Override
594    public RDF4JDataset createDataset() {
595        final Sail sail = new MemoryStore();
596        final Repository repository = new SailRepository(sail);
597        return rdf4j.createRepositoryDatasetImpl(repository, true, false);
598    }
599
600    @Override
601    public RDF4JGraph createGraph() {
602        return asGraph(new LinkedHashModel());
603    }
604
605    @Override
606    public RDF4JIRI createIRI(final String iri) throws IllegalArgumentException {
607        return asRDFTerm(getValueFactory().createIRI(iri));
608    }
609
610    @Override
611    public RDF4JLiteral createLiteral(final String lexicalForm) throws IllegalArgumentException {
612        final org.eclipse.rdf4j.model.Literal lit = getValueFactory().createLiteral(lexicalForm);
613        return asRDFTerm(lit);
614    }
615
616    @Override
617    public org.apache.commons.rdf.api.Literal createLiteral(final String lexicalForm, final org.apache.commons.rdf.api.IRI dataType)
618            throws IllegalArgumentException {
619        final org.eclipse.rdf4j.model.IRI iri = getValueFactory().createIRI(dataType.getIRIString());
620        final org.eclipse.rdf4j.model.Literal lit = getValueFactory().createLiteral(lexicalForm, iri);
621        return asRDFTerm(lit);
622    }
623
624    @Override
625    public org.apache.commons.rdf.api.Literal createLiteral(final String lexicalForm, final String languageTag)
626            throws IllegalArgumentException {
627        final org.eclipse.rdf4j.model.Literal lit = getValueFactory().createLiteral(lexicalForm, languageTag);
628        return asRDFTerm(lit);
629    }
630
631    @Override
632    public RDF4JTriple createTriple(final BlankNodeOrIRI subject, final org.apache.commons.rdf.api.IRI predicate, final RDFTerm object)
633            throws IllegalArgumentException {
634        final Statement statement = getValueFactory().createStatement(
635                (org.eclipse.rdf4j.model.Resource) asValue(subject), (org.eclipse.rdf4j.model.IRI) asValue(predicate),
636                asValue(object));
637        return asTriple(statement);
638    }
639
640    @Override
641    public Quad createQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final org.apache.commons.rdf.api.IRI predicate,
642            final RDFTerm object) throws IllegalArgumentException {
643        final Statement statement = getValueFactory().createStatement(
644                (org.eclipse.rdf4j.model.Resource) asValue(subject), (org.eclipse.rdf4j.model.IRI) asValue(predicate),
645                asValue(object), (org.eclipse.rdf4j.model.Resource) asValue(graphName));
646        return asQuad(statement);
647    }
648
649    public ValueFactory getValueFactory() {
650        return valueFactory;
651    }
652
653    private EnumSet<Option> optionSet(final Option... options) {
654        final EnumSet<Option> opts = EnumSet.noneOf(Option.class);
655        opts.addAll(Arrays.asList(options));
656        return opts;
657    }
658
659}