View Javadoc
1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements. See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.commons.rdf.rdf4j;
19  
20  import java.util.Arrays;
21  import java.util.EnumSet;
22  import java.util.Objects;
23  import java.util.Set;
24  import java.util.UUID;
25  
26  // To avoid confusion, avoid importing
27  // classes that are in both
28  // commons.rdf and openrdf.model (e.g. IRI, Literal)
29  import org.apache.commons.rdf.api.BlankNode;
30  import org.apache.commons.rdf.api.BlankNodeOrIRI;
31  import org.apache.commons.rdf.api.Dataset;
32  import org.apache.commons.rdf.api.Graph;
33  import org.apache.commons.rdf.api.Quad;
34  import org.apache.commons.rdf.api.RDFTerm;
35  import org.apache.commons.rdf.api.RDF;
36  import org.apache.commons.rdf.api.Triple;
37  import org.apache.commons.rdf.api.TripleLike;
38  import org.apache.commons.rdf.rdf4j.impl.InternalRDF4JFactory;
39  import org.eclipse.rdf4j.model.BNode;
40  import org.eclipse.rdf4j.model.IRI;
41  import org.eclipse.rdf4j.model.Literal;
42  import org.eclipse.rdf4j.model.Model;
43  import org.eclipse.rdf4j.model.Resource;
44  import org.eclipse.rdf4j.model.Statement;
45  import org.eclipse.rdf4j.model.Value;
46  import org.eclipse.rdf4j.model.ValueFactory;
47  import org.eclipse.rdf4j.model.impl.LinkedHashModel;
48  import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
49  import org.eclipse.rdf4j.repository.Repository;
50  import org.eclipse.rdf4j.repository.RepositoryConnection;
51  import org.eclipse.rdf4j.repository.sail.SailRepository;
52  import org.eclipse.rdf4j.sail.Sail;
53  import org.eclipse.rdf4j.sail.memory.MemoryStore;
54  
55  /**
56   * RDF4J implementation of RDF.
57   * <p>
58   * The {@link #RDF4J()} constructor uses a {@link SimpleValueFactory} to create
59   * corresponding RDF4J {@link Value} instances. Alternatively, this factory can
60   * be constructed with a different {@link ValueFactory} using
61   * {@link #RDF4J(ValueFactory)}.
62   * <p>
63   * {@link #asRDFTerm(Value)} can be used to convert any RDF4J {@link Value} to
64   * an RDFTerm. Note that adapted {@link BNode}s are considered equal if they are
65   * converted with the same {@link RDF4J} instance and have the same
66   * {@link BNode#getID()}.
67   * <p>
68   * {@link #createGraph()} creates a new Graph backed by {@link LinkedHashModel}.
69   * To use other models, see {@link #asGraph(Model)}.
70   * <p>
71   * To adapt a RDF4J {@link Repository} as a {@link Dataset} or {@link Graph},
72   * use {@link #asDataset(Repository, Option...)} or
73   * {@link #asGraph(Repository, Option...)}.
74   * <p>
75   * {@link #asTriple(Statement)} can be used to convert a RDF4J {@link Statement}
76   * to a Commons RDF {@link Triple}, and equivalent {@link #asQuad(Statement)} to
77   * convert a {@link Quad}.
78   * <p>
79   * To convert any {@link Triple} or {@link Quad} to to RDF4J {@link Statement},
80   * use {@link #asStatement(TripleLike)}. This recognises previously converted
81   * {@link RDF4JTriple}s and {@link RDF4JQuad}s without re-converting their
82   * {@link RDF4JTripleLike#asStatement()}.
83   * <p>
84   * Likewise, {@link #asValue(RDFTerm)} can be used to convert any Commons RDF
85   * {@link RDFTerm} to a corresponding RDF4J {@link Value}. This recognises
86   * previously converted {@link RDF4JTerm}s without re-converting their
87   * {@link RDF4JTerm#asValue()}.
88   * <p>
89   * For the purpose of {@link BlankNode} equivalence, this factory contains an
90   * internal {@link UUID} salt that is used by adapter methods like
91   * {@link #asQuad(Statement)}, {@link #asTriple(Statement)},
92   * {@link #asRDFTerm(Value)} as well as {@link #createBlankNode(String)}. As
93   * RDF4J {@link BNode} instances from multiple repositories or models may have
94   * the same {@link BNode#getID()}, converting them with the above methods might
95   * cause accidental {@link BlankNode} equivalence. Note that the {@link Graph}
96   * and {@link Dataset} adapter methods like
97   * {@link #asDataset(Repository, Option...)} and
98   * {@link #asGraph(Repository, Option...)} therefore uses a unique {@link RDF4J}
99   * internally.
100  *
101  * @see RDF
102  *
103  */
104 @SuppressWarnings("PMD.UnnecessaryFullyQualifiedName") // we use fully-qualified names for clarity
105 public 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 }