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